How to Convert VCF to FASTA
By Dr. Zubair Khalid, DVM, MS, PhD ·

By the end of this guide you will be able to take a VCF file, decide which FASTA sequence you actually need, and produce a checked consensus FASTA or variant-centered FASTA with bcftools, samtools, and bedtools. You need four things on hand: the VCF, the exact reference FASTA used for variant calling, a sample name if you want a sample-specific consensus, and a working command-line environment with bcftools, samtools, bedtools, and bgzip installed.
The phrase "convert VCF to FASTA" hides an important decision. A VCF stores variant sites and genotypes. A FASTA stores full sequences. You cannot convert one record into the other without a reference sequence and a rule for what to do with each variant. VCF is the common format for next-generation sequencing variant data, and conversion tools translate each VCF row into another representation while preserving the variant context [1]. This guide shows the practical routes, the commands that work, and the points where a naive conversion gives a biologically wrong answer.
What VCF and FASTA Actually Store
Bioinformatics file formats are built for different jobs. Treating them as interchangeable containers causes most conversion errors.
VCF stores differences, not full chromosomes
A VCF, or Variant Call Format file, is a text file with a header and one line per variant. The header defines the reference contigs and often the genome build. Each data line has columns for chromosome, position, identifier, reference allele, alternate allele, quality, filter status, INFO fields, FORMAT fields, and sample genotypes. A genotype such as 0/1 means one reference allele and one alternate allele at that site.
A contig is one contiguous sequence in a reference assembly, such as chr1. A single nucleotide variant, or SNV, changes one base. An indel is an insertion or deletion. A multi-allelic site has more than one alternate allele at the same position.
A VCF is the lingua franca of next-generation sequencing, but it is not a sequence file [1]. It tells you where a sample differs from a reference. It does not contain the full reference sequence, the full read sequences, or per-base quality scores for the sample.
FASTA stores sequences without quality scores
A FASTA file has records that begin with a header line starting with >, followed by sequence lines. FASTA is used for reference genomes, contigs, transcripts, proteins, aligned sequences, and consensus sequences. It carries letters, not per-base quality scores. FASTA is also a common input to downstream population-genetic tools, and conversion tools such as Fasta2Structure use Biopython to read FASTA records and transform them into other formats [2].
A FASTA header should be unique and informative. A header such as >chr1 is fine for a reference. A header such as >sample1_chr1_consensus is better when you generate many sample-specific files.
FASTQ stores raw reads plus quality scores
A FASTQ file stores raw sequencing reads. Each record has a header starting with @, a sequence line, a separator line starting with +, and a quality line with one score per base. FASTQ is the usual output of a sequencer. FASTA has no quality line. VCF has no reads and no per-base quality scores. The table below summarizes the difference.
| Format | Main content | Per-base quality | Typical use |
|---|---|---|---|
| VCF | Variant positions, REF and ALT alleles, genotypes, filters | No | Variant calling, association testing, annotation |
| FASTA | Full sequence letters | No | Reference genomes, consensus sequences, alignments, proteins |
| FASTQ | Raw reads plus quality scores | Yes | Read mapping, quality control, variant calling input |
FASTQ vs FASTA is a one-way simplification in most workflows. Converting FASTQ to FASTA drops quality scores. Converting VCF to FASTA drops genotype and filter information unless you encode it in the FASTA header or create one FASTA per sample.
Conversion changes the data model
File format libraries can parse, store, and convert many formats, but the scientific meaning still comes from the data model [3]. A VCF record for a heterozygous SNV has two possible alleles. A single FASTA sequence has one base at that position. Something must give: you choose one allele, use an IUPAC ambiguity code, split the sample into haplotypes, or keep the site in a separate table. A haplotype is one of the two chromosome copies in a diploid sample. Phased genotypes assign alleles to specific haplotypes. Unphased genotypes do not.
Conversion from imputation output to VCF shows the same problem. Tools must decide how to represent best-guessed genotypes, genotype posterior probabilities, and genotype dosage [4]. A VCF to FASTA conversion has an analogous decision: which allele becomes the sequence.
Decide Which FASTA You Need Before Running Anything
There are five common outputs. Pick one before you write commands.
| Desired FASTA | What it contains | Recommended route | Important caveat |
|---|---|---|---|
| Reference FASTA | The reference sequence with no variants applied | samtools faidx on the reference | This is not a conversion of the VCF |
| Sample consensus FASTA | Reference sequence with one sample's genotypes applied | bcftools consensus | Heterozygous sites need a policy |
| Haplotype FASTA | One phased chromosome copy with its variants applied | bcftools consensus with haplotype selection | Requires phased genotypes |
| Variant-centered FASTA | Short reference windows around each variant | bedtools getfasta after building a BED file | Gives reference sequence, not alternate allele sequence by default |
| Allele-specific FASTA | Each REF or ALT allele plus flanks | Consensus or custom substitution script | Indels and multi-allelic sites need care |
A reference FASTA is useful for checking coordinates. A sample consensus is useful for downstream alignment, primer design, or reporting a sample-specific sequence. A variant-centered FASTA is useful for motif analysis or visual inspection. An allele-specific FASTA is useful when you need to compare the reference and alternate sequences side by side.
If you need protein FASTA, stop and check your input. A genomic VCF alone does not contain transcript models, reading frames, or codon phase. You need a transcript annotation and a variant annotation step before you can produce coding or protein sequences. A simple VCF to FASTA command will not do that correctly.
Step-by-Step VCF to FASTA Walkthrough
This walkthrough uses a compressed VCF named variants.vcf.gz and a reference named reference.fa. The sample name is sample1. Change those names to match your files. The commands use short options to keep the lines readable.
Step 1: Inspect the VCF header and samples
Start by checking the file format, contigs, and sample names.
bcftools view -h variants.vcf.gz | head
bcftools query -l variants.vcf.gz
Expected header output looks like this.
##fileformat=VCFv4.2
##contig=<ID=chr1,length=248956422>
##contig=<ID=chr2,length=242193529>
#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT sample1
Expected sample output looks like this.
sample1
If bcftools query -l prints nothing, the VCF is sites-only and has no sample genotypes. A sites-only VCF can still be used for reference windows or allele-specific extraction, but it is not suitable for a sample consensus.
Check the reference build in the header and compare it with the FASTA you plan to use. A genome build label such as GRCh37 or GRCh38 matters because coordinates and REF alleles differ between builds [1]. If the VCF says GRCh38 and the FASTA is GRCh37, stop and get the correct reference.
Step 2: Index the reference FASTA
Both samtools and bedtools need a FASTA index. The index is a small .fai file that records sequence names, lengths, and byte offsets.
samtools faidx reference.fa
cut -f1,2 reference.fa.fai | head
Expected output looks like this.
chr1 248956422
chr2 242193529
The first column is the contig name. The second column is the contig length in base pairs. Check that these names match the VCF contig names exactly. chr1 and 1 are different names to these tools. A mismatch causes failed lookups even when the underlying assembly is the same.
Step 3: Compress and index the VCF
Most bcftools operations work best with a block-compressed and indexed VCF. Block compression uses bgzip, which is related to gzip but supports random access through an index.
bgzip -c variants.vcf > variants.vcf.gz
bcftools index -t variants.vcf.gz
If the VCF is already compressed, skip the bgzip line and run the index command. The -t option creates a tabix index with the .tbi suffix. For very large chromosomes or many contigs, a CSI index may be needed. In that case, use bcftools index without -t.
If you forget the index, you may see this exact error.
[E::idx_find_and_load] Could not retrieve index file for 'variants.vcf.gz'
Fix it with bcftools index -t variants.vcf.gz.
Step 4: Normalize the VCF against the reference
Normalization left-aligns indels and splits multi-allelic records so each record has one ALT allele. It also checks that the REF allele in the VCF matches the reference FASTA.
bcftools norm -f reference.fa -m -any -Oz -o variants.norm.vcf.gz variants.vcf.gz
bcftools index -t variants.norm.vcf.gz
The -f reference.fa option supplies the reference. The -m -any option splits multi-allelic sites. The -Oz option writes compressed VCF output. The -o option names the output.
If a REF allele does not match the FASTA, bcftools reports a mismatch at a specific chromosome and position. Do not ignore that message. It usually means the wrong reference build, the wrong contig naming, or a corrupt VCF. Fix the reference before you continue.
Step 5: Filter to the variants you trust
A VCF can contain low-quality calls, filtered sites, and variants outside the region you care about. For a consensus sequence, decide which records should be applied. A common starting point is to keep only records with PASS in the FILTER column.
bcftools view -f PASS -Oz -o variants.pass.vcf.gz variants.norm.vcf.gz
bcftools index -t variants.pass.vcf.gz
The -f PASS option includes sites whose FILTER value is PASS. If your VCF uses a different filter label, adjust the command. If you want to exclude sites with low quality, add a quality threshold with bcftools view -i 'QUAL>=30'. Use a threshold that matches your calling pipeline and project. Do not copy a number from another project without checking its meaning.
Expected output is a smaller VCF with the same header and only the records that pass the filter.
chr1 101 . A G 60 PASS . GT 0/1
chr1 250 . C T 55 PASS . GT 1/1
Step 6: Build a sample consensus FASTA
A consensus sequence is the reference sequence with selected variants applied for one sample. The bcftools consensus command does this directly.
bcftools consensus -f reference.fa -s sample1 -o sample1.consensus.fa variants.pass.vcf.gz
The -f reference.fa option supplies the reference. The -s sample1 option selects the sample. The -o sample1.consensus.fa option names the output. If the VCF has only one sample, you can omit -s, but naming the sample is safer and easier to audit.
Expected output is FASTA with one record per reference contig.
>chr1
ACGT...G...ACGT
>chr2
TTAC...A...GGCA
The number of FASTA records should equal the number of contigs in the reference, not the number of variants in the VCF. The tool reports how many variants it applied. A small number compared with the VCF record count is normal if many records were filtered out or are not present in the selected sample.
For heterozygous sites, decide on a policy before you use the output. A diploid sample can carry two different alleles at one position. A single consensus FASTA cannot show both without an ambiguity code or a haplotype split. An IUPAC ambiguity code represents more than one possible base, such as R for A or G. If you need true haplotype sequences, the VCF must be phased and you must select the haplotype deliberately. Do not treat an unphased heterozygous consensus as a true chromosome sequence.
Step 7: Extract variant-centered reference windows
Sometimes you do not want a whole chromosome. You want a short FASTA window around each variant for motif inspection, primer design, or manual review. Build a BED file from the VCF, add flanks, and extract the reference sequence.
A BED file is a tab-delimited file with chromosome, zero-based start, end, and name columns. BED start is zero-based. VCF position is one-based. The conversion below handles that offset and uses the length of the REF allele to cover the full variant interval.
bcftools query -f '%CHROM\t%POS\t%REF\t%ALT\n' variants.pass.vcf.gz |
awk 'BEGIN{OFS="\t"} {start=$2-1; end=start+length($3); print $1,start,end,$1":"$2}' > variants.bed
Expected variants.bed content looks like this.
chr1 100 101 chr1:101
chr1 249 250 chr1:250
Create a genome file with contig names and lengths, then add 50 bp of flanking sequence on each side.
cut -f1,2 reference.fa.fai > reference.genome
bedtools slop -i variants.bed -g reference.genome -b 50 > variants.windows.bed
The -b 50 option adds 50 bp to both sides. For a one-base SNV, the final window is 101 bp. For a deletion with a REF allele of length 10, the final window is 110 bp. The formula is window length = REF length + 2 times the flank length. bedtools clips windows at contig boundaries, so a variant near the start or end of a contig can produce a shorter window.
Extract the reference sequence with bedtools.
bedtools getfasta -fi reference.fa -bed variants.windows.bed -nameOnly -fo variants.windows.fa
The -fi option supplies the reference FASTA. The -bed option supplies the BED file. The -nameOnly option uses the BED name as the FASTA header. The -fo option names the output.
Expected output looks like this.
>chr1:101
NNNNNACGTACGT...G...ACGTACGTNNNNN
>chr1:250
NNNNNTTGAC...C...GTTACNNNNN
This output is reference sequence. It does not contain the alternate allele. If you need an allele-specific window, edit the REF allele to the ALT allele for simple SNVs and indels, then validate the result against the VCF. Do not assume that bedtools getfasta applied the variant. It did not.
Step 8: Check the output before downstream use
Run three checks. First, count FASTA records. Second, check sequence lengths. Third, spot-check a known variant.
grep -c '^>' sample1.consensus.fa
samtools faidx sample1.consensus.fa
cut -f1,2 sample1.consensus.fa.fai | head
For a consensus FASTA, the record count should match the reference contig count. The lengths should match the reference lengths unless the VCF contains large structural variants that change contig length. For a window FASTA, the record count should match the number of BED intervals.
Spot-check a variant by extracting the same coordinate from the reference and the consensus.
samtools faidx reference.fa chr1:101-101
samtools faidx sample1.consensus.fa chr1:101-101
If the VCF has a homozygous alternate genotype 1/1 and ALT is G, the consensus should show G at that position. If the genotype is 0/1, the result depends on your heterozygote policy. Use a variant with a clear homozygous genotype for the first check.
Key Options and Formulas
The table below summarizes the commands that matter most.
| Goal | Command or tool | Key options | Output | Caveat |
|---|---|---|---|---|
| Index reference | samtools faidx | FASTA path | .fai index | Contig names must match VCF |
| Compress VCF | bgzip | -c | .vcf.gz | Does not sort or normalize |
| Index VCF | bcftools index | -t for tabix | .tbi index | Needed for random access |
| Normalize | bcftools norm | -f, -m -any, -Oz | Normalized VCF | REF mismatch means wrong reference |
| Filter | bcftools view | -f PASS, -i | Filtered VCF | Filter choice changes biology |
| Consensus | bcftools consensus | -f, -s, -o | Sample FASTA | Heterozygotes need a policy |
| Windows | bedtools getfasta | -fi, -bed, -nameOnly, -fo | Window FASTA | Gives reference sequence only |
| Flank size | bedtools slop | -b 50 | Expanded BED | Window length = REF length + 100 |
Practical Tips for a Clean VCF to FASTA Conversion
These tips reduce the most common VCF to FASTA errors.
- Use the exact reference FASTA that was used for variant calling. A different patch release can change REF alleles and coordinates.
- Normalize before consensus. Split multi-allelic records and left-align indels so each record has one clear ALT allele.
- Keep the original VCF. FASTA output loses genotype, quality, filter, and phasing details.
- Decide the heterozygote policy before you run the command. Options include IUPAC ambiguity codes, one haplotype, or a simplified consensus.
- Filter variants deliberately. Applying every record in a VCF can put low-quality calls into a sequence that looks authoritative.
- Use unique FASTA headers. Include sample, contig, and build when the file will be shared.
- Check contig naming first.
chr1and1are not interchangeable to bcftools, samtools, or bedtools. - Run a small test region before a whole-genome conversion. A one-contig test catches index, naming, and REF mismatch problems quickly.
- Record tool versions and command lines. Workflow systems can help with reproducibility and audit traces, which matters when sequences move into reports or publications [5].
- Validate a few sites by hand. Pick one homozygous SNV, one heterozygous SNV, and one indel.
Common Mistakes and Limitations
Treating VCF as a sequence file
The most basic mistake is expecting a one-step format conversion. A VCF does not contain the full sequence. It contains variant sites against a reference. You need the reference and a rule for applying variants.
Using the wrong reference build
A VCF called against GRCh38 cannot be applied safely to a GRCh37 FASTA. The REF allele check may fail, or worse, a variant may apply at a coordinate that happens to match by chance. Always confirm the build and the contig naming.
Ignoring REF allele mismatches
When bcftools norm or bcftools consensus reports a REF mismatch, do not skip the record to make the command run. A mismatch is a signal that the reference, the VCF, or both are wrong. Fix the cause.
Forgetting to index the VCF
The error [E::idx_find_and_load] Could not retrieve index file for 'variants.vcf.gz' means the VCF is not indexed. Run bcftools index -t variants.vcf.gz. If the VCF is not block-compressed, compress it with bgzip first.
Applying filtered or low-quality variants
A FASTA sequence hides filter labels. If you apply every VCF record, a low-quality call becomes a base in the final sequence. Filter first, and keep a record of the filter expression you used.
Assuming a consensus is a true haplotype
A diploid sample has two chromosome copies. A single consensus sequence collapses them. For heterozygous sites, you need ambiguity codes or phased haplotypes. An unphased consensus is not a true chromosome sequence.
Expecting alternate alleles from bedtools getfasta
bedtools getfasta extracts reference sequence from a BED interval. It does not apply ALT alleles. If you need alternate allele windows, you must substitute the REF allele or use a consensus route.
Mishandling multi-allelic and structural variants
Multi-allelic sites have more than one ALT allele. A single FASTA sequence cannot represent all of them at once. Split multi-allelic records first, then decide which allele to apply. Structural variants with symbolic alleles such as <DEL> and breakend records need specialized handling. Do not assume a simple consensus command covers every complex event.
Losing metadata
FASTA headers can carry sample and contig names, but they are not a full replacement for the VCF. Keep the VCF, the reference, the command line, and the filter policy together. Sequence format conversions often need metadata to remain useful, as seen in sequence database submission workflows that convert EMBL or GenBank records into submission-ready checklists [6].
Forgetting that FASTA has no quality scores
FASTA cannot store per-base quality. If you need quality assessment, keep the FASTQ or the VCF quality fields. Converting to FASTA is a lossy step for quality information.
Using the wrong sample
A multi-sample VCF can contain many genotypes. bcftools consensus -s sample1 applies one sample. If you omit -s and the VCF has multiple samples, you may not get the sample you intended. Check bcftools query -l first.
Not checking sequence lengths
A consensus FASTA should have contig lengths that match the reference unless large events were applied. A window FASTA should have lengths that match the BED intervals after clipping. Run samtools faidx and inspect the .fai file.
Frequently Asked Questions
Can I convert a VCF to FASTA without a reference genome?
No. A genomic VCF stores variants relative to a reference, so you need the reference sequence to reconstruct a full sequence. Without a reference you can only reformat the variant table, not create a valid genome-scale FASTA.
Does VCF to FASTA keep genotype information?
Usually no. Standard FASTA stores sequence letters only. If you create one consensus per sample, the sample identity can be placed in the header, but the genotype, quality, and filter fields are lost unless you keep the VCF.
What is the difference between FASTQ and FASTA?
FASTQ stores raw sequencing reads with one quality score per base. FASTA stores sequences without quality scores. FASTQ is used before mapping and variant calling. FASTA is used for references, consensus sequences, alignments, and proteins.
Which tool should I use for VCF to FASTA?
Use bcftools consensus when you need a sample-specific consensus sequence with variants applied. Use bedtools getfasta when you need reference windows around variant sites. Use samtools faidx when you only need reference sequence.
Why does bcftools consensus reject or skip variants?
Common reasons are a REF allele mismatch, a wrong reference build, a contig name mismatch, an unsorted or unnormalized VCF, a missing index, or a sample name that is not present. Fix the reference and normalize the VCF before rerunning.
Can I convert a single VCF variant to FASTA?
Yes. Extract a window around the variant from the reference with samtools faidx or bedtools getfasta, then substitute the REF allele with the ALT allele for a simple SNV or indel. Validate the edited sequence against the VCF record.
Does converting VCF to FASTA lose variants?
A reference-only FASTA loses all variants by design. A sample consensus applies selected variants but collapses heterozygous sites and drops other samples. Keep the VCF as the primary record of variants.
How do I convert FASTQ to FASTA?
Use a read conversion tool such as seqtk seq -a reads.fastq > reads.fasta. This drops per-base quality scores. FASTQ to FASTA is a separate operation from VCF to FASTA and does not use variant coordinates.
Sources
- vcf2fhir: a utility to convert VCF files into HL7 FHIR format for genomics-EHR integration.
- Fasta2Structure: a user-friendly tool for converting multiple aligned FASTA files to STRUCTURE format.
- IOData: A python library for reading, writing, and converting computational chemistry file formats and generating input files.
- GEN2VCF: a converter for human genome imputation output format to VCF format.
- SciPipe: A workflow library for agile development of complex and dynamic bioinformatics pipelines.
- EMBL2checklists: A Python package to facilitate the user-friendly submission of plant and fungal DNA barcoding sequences to ENA.