Written and maintained by CASRAI Editorial Board
Last updated
A VCF (Variant Call Format) file is a plain-text, tab-delimited record of the genetic variants found at specific positions in a genome, relative to a reference sequence. It is the standard output of variant callers such as GATK’s HaplotypeCaller, and the standard input to every downstream step — annotation, filtering, cohort-level joint genotyping, or loading into a variant database. Most of the confusion people run into with VCF isn’t the format’s basic layout, which is simple: it’s misreading the genotype columns at the far right of each line, where a single symbol like 0/1 versus 1|1 changes what the file is actually claiming about a sample.
The three-part structure: meta-information, header, and data lines
Every VCF file is built from three kinds of lines, always in this order:
- Meta-information lines, each starting with
##. These declare file-level metadata — the format version (##fileformat=VCFv4.2), the reference genome used, and — critically — a definition line for every key that appears later in the INFO and FORMAT fields. A meta-information line like##INFO=<ID=DP,Number=1,Type=Integer,Description="Total Depth">is what tells a parser (and a human reader) what theDPkey in the INFO field actually means. Skipping past these lines without reading them is the single most common reason people misinterpret a VCF they didn’t generate themselves — the same key can be defined differently by different callers. - The column header line, exactly one line, starting with a single
#:#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT sample1 sample2 .... This is the line that names every column that follows. - Data lines, one per variant record, no leading
#, tab-separated, following the column order the header line just declared.
The 8 fixed columns, and what each one holds
Every data line starts with the same 8 mandatory columns, in this exact order:
- CHROM — the chromosome or contig the variant sits on, matching a sequence name in the reference used to call it.
- POS — the 1-based position of the variant on that sequence (VCF positions count from 1, not 0 — unlike BED’s 0-based, half-open coordinates, which is a common source of off-by-one errors when converting between the two formats).
- ID — a known identifier for the variant if one exists (most often a dbSNP rs number), or a literal
.if it isn’t in any reference database. - REF — the reference allele: the base or bases present at this position in the reference genome.
- ALT — the alternate allele(s) observed, comma-separated if there’s more than one candidate alternate at the same position.
- QUAL — a Phred-scaled quality score for the assertion that a variant exists at this site at all (this is a separate number from the per-base Phred quality scores attached to individual sequencing reads — QUAL scores the variant call itself, not a base call).
- FILTER — either
PASS, or the name(s) of whichever quality filter(s) this record failed; a literal.means no filtering was applied at all, which is not the same thing as passing. - INFO — an extensible, semicolon-delimited set of site-level annotations (covered below).
Two optional columns can follow: FORMAT, and then one column per sample in the callset — also covered below, since this is where most misreading happens.
The INFO field: semicolon-delimited key=value annotations
The INFO column packs an arbitrary number of site-level annotations into one field, using the pattern key=value pairs separated by semicolons, e.g.:
DP=87;AF=0.5;AC=1;AN=2;MQ=59.8
Some keys are flags with no value at all (their presence alone means something, e.g. DB to mark a variant as present in dbSNP). What each key means is defined back in the ##INFO=<...> meta-information lines at the top of the file — there is no fixed universal key set beyond a handful of reserved keys the spec recommends (like AF for allele frequency, DP for combined depth, AC/AN for allele/total allele counts). A caller is free to define its own additional INFO keys, which is exactly why the header definitions matter: two files with an identical-looking MQ=59.8 can mean subtly different things if the callers that produced them defined MQ differently.
FORMAT and the per-sample genotype columns — the part people misread
When a VCF reports one or more samples, the INFO column is followed by a FORMAT column and then one column per sample. FORMAT is a colon-delimited list of keys (e.g. GT:AD:DP:GQ:PL) that defines, in order, what each sample’s column contains — every sample column then holds colon-delimited values matching that same key order, e.g. 0/1:23,19:42:99:571,0,528.
The first FORMAT key is almost always GT (genotype), and it’s the piece people get wrong most often:
- GT encodes a sample’s genotype as allele indices, not allele sequences.
0means the REF allele;1means the first ALT allele listed;2would mean the second ALT allele if the site is multi-allelic, and so on. - 0/0 is homozygous reference — both copies match REF, i.e. no variant call at this site for this sample.
- 0/1 is heterozygous — one REF copy, one copy of the first ALT allele.
- 1/1 is homozygous alternate — both copies carry the first ALT allele.
- The separator between the two allele indices is what encodes phase, and this is the single most consistently misread character in a VCF. A forward slash (
/) means unphased: the caller knows the sample carries these two alleles but does not know which chromosome copy (maternal or paternal) each one sits on. A pipe (|) means phased: the order left-to-right is known and meaningful —0|1and1|0describe two genuinely different haplotype arrangements, whereas0/1and1/0describe the exact same unphased call and are interchangeable. Phasing information doesn’t come from the genotype call itself; it’s added by a separate phasing step (statistical population-based phasing, read-backed phasing from long reads, or pedigree/trio-based phasing), so an unphased caller’s raw output will always use/even at heterozygous sites.
Treating a phased pipe as if it were an unphased slash (or vice versa) silently changes what a genotype comparison or a haplotype-based downstream analysis is allowed to conclude — which is exactly the kind of error that doesn’t throw a parsing exception, it just produces a quietly wrong answer.
A worked, illustrative example line
The line below is a generic, illustrative example built to show the structure described above — it is not drawn from any real sample, study, or dataset.
##fileformat=VCFv4.2
##INFO=<ID=DP,Number=1,Type=Integer,Description="Total Depth">
##INFO=<ID=AF,Number=A,Type=Float,Description="Allele Frequency">
##FORMAT=<ID=GT,Number=1,Type=String,Description="Genotype">
##FORMAT=<ID=DP,Number=1,Type=Integer,Description="Read Depth">
#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT SAMPLE1
chr7 140453136 . A T 89 PASS DP=42;AF=0.5 GT:DP 0/1:42
Reading this record left to right: a variant on chromosome 7 at position 140,453,136, with no known dbSNP ID, where the reference base A is sometimes replaced by T; the call passed all filters, with a site quality score of 89; the site has a combined depth of 42 reads across an allele frequency of 0.5; and SAMPLE1 is heterozygous (0/1) at 42x depth for this sample specifically.
Frequently asked questions
Why does the same INFO key sometimes mean different things in files from different tools?
Because INFO and FORMAT keys beyond a small reserved set are not fixed by the VCF specification itself — each caller defines its own keys in the ##INFO/##FORMAT meta-information lines at the top of its output. Always check those definition lines in the specific file you’re reading rather than assuming a key means what it meant in a different tool’s output.
What does a genotype of ./. mean?
A GT value of ./. (or . for a haploid call) means no genotype could be called for that sample at that site — typically because coverage was too low or the region wasn’t assessed — not that the sample is homozygous reference. Confusing a missing call with a confirmed reference call is a common downstream analysis error; a gVCF’s reference blocks exist specifically to make that distinction explicit across a cohort.
Can a single VCF line describe more than one alternate allele?
Yes — ALT can list multiple comma-separated alleles for a multi-allelic site, and GT then indexes into that list (2 refers to the second listed ALT allele, and so on). Many tools expect VCFs to be split into one line per alternate allele (bi-allelic) before further processing, since not every downstream tool handles multi-allelic records correctly.
Is a VCF the same thing as a gVCF?
No. A standard VCF records only variant sites. A gVCF is the same underlying format extended with “reference blocks” — banded records covering confidently non-variant regions — which is what lets a joint-genotyping step distinguish “confidently reference” from “never assessed” across every sample in a cohort. See gVCF vs VCF for the full comparison.
Related reading
- gVCF vs VCF: Reference Blocks and Joint Genotyping
- What Is a BAM File? Format, Structure, and Tools
- BAM Index (.bai) Files: Why You Need One and How to Build It
- BED File Format: The 0-Based, Half-Open Coordinate System Explained
- Phred Quality Scores: How Q Values Encode Base-Call Error Probability
- FASTQ Format Explained: Structure, Quality Encoding, and Header Fields
- Decoding SAM/BAM FLAG Values: A Practical Guide







