Chatomics Field GuideWhat They Don't Teach You

Sanity check · Variant Calling

How to Detect Contamination in Variant Calling

A low mapping rate isn't "bad sequencing" until you've checked what the unmapped reads actually are.

By Ming "Tommy" Tang, Director of Bioinformatics in Big Pharma · Reviewed September 2026 · 5 min read

You've just finished alignment and joint calling, and something is off. Maybe the mapping rate came back lower than the rest of the batch and you assumed it was a bad sequencing run. Maybe the VCF has more heterozygous calls than you'd expect, or a somatic sample's allele fractions are smeared instead of clustering where tumor purity says they should. Before you spend a day debugging your calling parameters, ask a more basic question: are these reads actually from the organism, tissue and sample you think they are?

Contamination is quiet. It doesn't throw an error. It just sits in your BAM and VCF, inflating false-positive heterozygous calls, corrupting the allele-fraction model your somatic caller depends on, and in the worst documented cases turning into a published finding that was actually Mycoplasma DNA, a mismatched cell line, or another patient's reads bleeding across a flow cell. Roughly 15-35% of cultured cell lines carry Mycoplasma, and HeLa cross-contamination alone is implicated in tens of thousands of papers built on misidentified lines.

This page gives you a screening order, cheapest checks first, that you can run against your own BAM and VCF in the next hour, plus the fixes that actually apply once you've confirmed a specific contamination source, and the cases where what looks like contamination is just correct biology.

What it looks like when it's happening

  • Mapping rate well below your batch's historical baseline for a clean human WES or WGS library, which you initially chalk up to a bad sequencing run
  • A GATK CalculateContamination table reporting a non-trivial, non-zero contamination fraction at common biallelic SNP sites
  • Excess heterozygous calls, or a somatic allele-fraction distribution that's smeared instead of clustering at the expected tumor-purity value
  • Unmapped reads that, when remapped, cluster cleanly onto a bacterial genome instead of scattering as adapter and low-complexity junk
  • Viral sequence (e.g. HPV18) turning up in a tumor type it has no biological business being in
  • Heterozygous genotypes called from RNA-seq that don't match the WES or WGS genotypes for the same nominal subject
  • Low-frequency variant calls at positions that are high-depth, private calls in a different sample processed on the same flow cell lane
  • A mouse-mapping read fraction in a PDX or xenograft sample that's inconsistent with the model's expected tumor purity

Why it happens

Contamination enters at several distinct points, and each one leaves a different footprint. Cell culture picks up Mycoplasma from air, media or a neighboring flask, or gets quietly overrun by an aggressively proliferating line like HeLa. Library prep carries over ribosomal RNA when depletion or polyA selection is incomplete. Sequencing on patterned flow cells like NovaSeq lets free adapters and indices misassign a small fraction of reads between multiplexed samples in the same lane (index hopping). And some study designs are a genuine mixture by intent, a PDX or xenograft tumor grown in a mouse host will always include mouse stromal reads.

The genomic signature depends on how closely the contaminant relates to your reference. Reads from a different organism either fail to align at all and pile up in the unmapped bucket, or, if similar enough, mismap into low-mappability and repetitive regions where they get miscalled as spurious low-VAF variants. Reads from a different human sample, whether cross-culture contamination or index hopping, map to your reference just fine. They simply carry alleles that don't belong to your subject's genotype, which shows up as extra heterozygous calls, an allele-fraction distribution that doesn't sit where it should, and a measurable contamination fraction at population-common SNP sites (this is exactly what GATK CalculateContamination and VerifyBamID2 measure, by comparing observed reference-allele support at homozygous-alt sites against the population-expected frequency).

This matters specifically for variant calling because callers like HaplotypeCaller and Mutect2 assume the reads at a site came from one diploid genome (or one clean tumor/normal pair). Any admixture of a second genome violates that assumption directly: truly homozygous sites start looking heterozygous, and heterozygous sites shift away from the expected 0.5 allele balance. That's the mechanism behind almost every symptom on this page, from the excess-heterozygosity signal to the smeared somatic VAF distribution.

The checks

Run them in order. Each one tells you what healthy looks like and what the problem looks like.

0/7 checked · saved in this browser

  1. Run samtools flagstat (or pull the mapping rate out of MultiQC) on the BAM right after alignment, before you trust anything downstream. Compare against the rest of the batch and your own pipeline's historical distribution, not a number from a paper.

    bash
    samtools flagstat sample.bam
    Healthy
    A high mapping rate that's consistent with the rest of your batch and your pipeline's historical baseline for that assay.
    Red flag
    A mapping rate noticeably below the rest of the batch. This is the cheapest tell you have; one documented case had over 50% of reads mapping to Mycoplasma instead of human.
  2. Extract unmapped reads to FASTQ and remap them against a small library of likely contaminants (Mycoplasma species, an rRNA reference, mouse genome if you work with xenografts) instead of assuming they're just noise.

    bash
    samtools view -b -f 4 sample.bam > unmapped.bam
    samtools fastq unmapped.bam > unmapped.fastq
    bwa mem mycoplasma_refs.fa unmapped.fastq | samtools flagstat -
    Healthy
    Unmapped reads scatter across adapter dimer, low-complexity sequence and reference gaps, with no single non-target genome absorbing a large share of them.
    Red flag
    A large fraction of the unmapped reads remap cleanly onto one non-target genome. A validated approach for Mycoplasma specifically filters reads through the 16S rRNA sequence of the reference organism first, then maps what's left against a Mycoplasma database, then de novo assembles the remainder to confirm the species.
  3. Align (or realign a subset) to human and mouse references separately, or to a combined reference, and compare the per-sample read fraction assigned to each genome.

    Healthy
    A stable, expected mouse-stromal read fraction that's consistent across replicates and matches the known composition of that model.
    Red flag
    The human:mouse ratio swings between samples that should be biologically similar, or a sample you expected to be near-pure tumor shows an unexplained jump in mouse-origin reads.
  4. Run GetPileupSummaries to build a pileup table at common biallelic population SNP sites, then feed it to CalculateContamination to estimate the fraction of reads from a non-self source.

    bash
    gatk GetPileupSummaries \
      -I sample.bam \
      -V common_biallelic.vcf.gz \
      -L common_biallelic.vcf.gz \
      -O sample.pileups.table
    
    gatk CalculateContamination \
      -I sample.pileups.table \
      -O sample.contamination.table
    Healthy
    A contamination fraction close to zero in the output table, computed from reference-allele support at homozygous-alt sites, using GATK's default minimum of 10 reads per site and relative-coverage bounds of 0.5x to 3.0x around the sample's median/mean coverage.
    Red flag
    A non-trivial, non-zero contamination fraction. GATK doesn't publish one universal pass/fail number, so judge it against your own pipeline's historical distribution or your lab's validated clinical threshold.
  5. Calculate per-sample heterozygosity or inbreeding coefficient (F) from the called VCF and compare each sample against the rest of the cohort's distribution.

    bash
    vcftools --vcf sample.vcf --het --out sample_het
    Healthy
    F consistent with the cohort's known ancestry and relatedness structure, no consistent outlier.
    Red flag
    An unusually low (more negative) F relative to the rest of the cohort. Excess heterozygosity is the signature of a second genome mixed into the sample.
  6. If you have both RNA-seq and DNA-seq for the same nominal subject, call heterozygous SNPs from expressed regions in the RNA-seq and compare genotype concordance against the DNA-based calls at the same positions.

    Healthy
    High genotype concordance between the two assays at shared, adequately covered positions.
    Red flag
    Substantial discordance. This catches both sample swaps and cross-sample contamination that a single-assay QC metric would miss on its own; the same approach can pull real DNA-level signal like a BRAF V600E straight out of RNA-seq.
  7. For any experiment resting on an established or long-passaged cell line, run short-tandem-repeat (STR) profiling and check it against a reference database (ATCC, DSMZ, Cellosaurus) instead of trusting the vial's label.

    Healthy
    The STR profile matches the expected reference line.
    Red flag
    The profile doesn't match, or matches a different well-known line entirely. An estimated 20-36% of cell lines are misidentified, largely from HeLa cross-contaminating other cultures, and HeLa-specific HPV18 viral integrants have turned up misattributed to unrelated TCGA tumor types.

What to do about it

Re-derive or replace the culture, don't try to clean the data around it

When: Mycoplasma or a known cross-contaminating line (e.g. HeLa) is confirmed by remapping or STR profiling

Stop using the current culture for anything that matters. Treat it with an anti-Mycoplasma reagent and re-test before resuming, or discard it outright and thaw a fresh, STR-authenticated stock. Repeat STR profiling before freezing new stocks and before submitting for publication.

Caveat: You lose the time invested in the contaminated culture, and any prior experiment built on it needs to be flagged or repeated. There's no computational fix once the underlying biology was wrong.

Feed the contamination estimate into variant filtering instead of a hand-picked VAF cutoff

When: CalculateContamination reports a real, non-trivial contamination fraction in an otherwise usable sample, common in somatic calling

Pass the contamination table into your caller's filtering step (e.g. Mutect2 / FilterMutectCalls) so low-VAF calls consistent with the estimated contamination level get filtered systematically, rather than applying an arbitrary VAF threshold after the fact.

Caveat: This manages contamination, it doesn't remove it. A heavily contaminated sample can still bury a real low-VAF driver mutation under the filter, so treat a high contamination fraction as a reason to resequence, not just refilter.

Move to unique dual indexes and tighter demultiplexing

When: You see the cross-sample, low-frequency variant-sharing signature of index hopping on a patterned flow cell

Re-library and resequence using unique dual index (UDI) adapters instead of combinatorial or single indexes, and tighten the index-mismatch tolerance at demultiplexing for future runs.

Caveat: This only fixes runs going forward. Variant calls already made from a hopped-index run should be treated as suspect at low allele fraction, not silently trusted.

Trace and correct sample swaps at the source, not computationally

When: RNA-seq/WES genotype concordance or STR profiling shows a sample doesn't match its label

Walk the mismatch back through the physical chain: sequencing manifest, aliquoting, sample receiving, freezer box position. Relabel or re-request the correct sample.

Caveat: This is slow, and it can implicate wet-lab steps you can't fully audit after the fact. Don't try to computationally reassign the sample's identity as a shortcut.

Fix rRNA or microbial carryover at the library-prep level, not just in the pipeline

When: rRNA or a specific microbial signature keeps showing up across many samples from the same prep batch or kit lot

Correct it upstream: use proper rRNA depletion or polyA selection appropriate to the RNA type for future preps, and add a routine remap-to-contaminant-reference step as an early QC gate in the pipeline.

Caveat: Computational filtering after the fact is a stopgap. It recovers usable reads but not the library complexity lost to the contaminating fraction, so a badly affected batch is often worth re-prepping instead of just filtering harder.

When not to "fix" it

Don't treat every off-target signal as an error to eliminate. Mouse-derived reads in a PDX or xenograft sample are the expected biology of that model, not contamination; the correct response is computational deconvolution, aligning to a combined or separate human/mouse reference and assigning reads per sample, not discarding or "cleaning" the mouse fraction. Genuine chimerism, such as a bone marrow transplant recipient or a twin study, produces two real genotypes in one sample, and that will trip a CalculateContamination-style metric or an excess-heterozygosity check exactly the way contamination does. Real subclonal tumor heterogeneity does the same thing to allele balance. Before you "correct" a sample, ask whether the specimen's own biology explains the signal; a post-transplant blood sample is supposed to show mixed genotypes, and forcing it through a contamination filter destroys the finding you actually wanted.

Five things experienced analysts do here

  1. Make mapping rate an automatic pipeline QC gate compared against your batch's own historical baseline, not something you eyeball once at the end.
  2. Keep a standing library of decoy references for the contaminants relevant to your work (common Mycoplasma species, an rRNA reference, mouse genome for xenograft work) so screening is a rerun, not a scramble under deadline pressure.
  3. For any project touching an established cell line, put STR profiling on a calendar, before freezing stocks and before submission, not on a 'when I remember' basis.
  4. Treat CalculateContamination and a heterozygosity/inbreeding check as standard columns in every sample QC table, not a one-off investigation triggered only after a reviewer asks.
  5. When a mapping rate looks off, pull the unmapped reads and look at what they are before requesting a resequence; the answer is often sitting right there in the reads you were about to throw away.

Questions people ask

What mapping rate should I expect for a clean human WES or WGS sample?

There's no single universal number the field agrees on, but a healthy short-read human exome or genome maps in the mid-90s percent or higher, and consistently so across a batch. Compare against your own pipeline's historical baseline rather than a number from a paper; a sample that maps meaningfully worse than the rest of the batch is the cheapest contamination tell you have.

How do I check for Mycoplasma contamination in sequencing data?

Pull the reads that failed to map to your target reference, convert them to FASTQ, and remap them against a Mycoplasma reference or database. A validated two-step approach first filters reads through the 16S rRNA sequence of the reference organism to strip non-specific hits, then maps what's left against a Mycoplasma database and assembles the remainder de novo to confirm the species. A large fraction of unmapped reads landing cleanly on Mycoplasma is the signature, not a coincidence.

What GATK contamination fraction from CalculateContamination is too high?

The literature doesn't converge on one universal cutoff. GATK's own documentation describes the methodology, a minimum of 10 reads per site and relative coverage bounds of 0.5x to 3.0x around the sample's median/mean coverage, without prescribing a single pass/fail threshold. Judge the number against your own pipeline's historical distribution or your lab's validated clinical cutoff, and for somatic calling feed it into filtering rather than eyeballing a fixed value.

Are mouse reads in a xenograft or PDX sample considered contamination?

No. Mouse stromal reads in a patient-derived xenograft are expected biology, since the tumor grows inside a mouse host and some mouse cells are always captured alongside the human tumor cells. The correct handling is computational deconvolution, aligning to human and mouse references and assigning reads per genome, not treating the mouse fraction as an error to remove.

Can I detect a sample swap using RNA-seq data instead of exome sequencing?

Yes. Heterozygous SNPs called from expressed regions of RNA-seq can be cross-checked against genotypes from matched WES or WGS for the same nominal subject. Strong genotype concordance confirms identity, and discordance flags either a sample swap or cross-sample contamination. The same idea has been used to pull real DNA-level variant signal, including specific hotspot mutations, directly out of RNA-seq.

Related pages

Related reading on the blog

Sources

  1. CalculateContamination, GATK — Parameters and methodology behind the GetPileupSummaries / CalculateContamination workflow (minimum 10 reads/site, 0.5x-3.0x relative coverage thresholds) used in the GATK contamination check.
  2. NGSTroubleFinder: a tool for detection and quantification of contamination and kinship across human NGS data — Newer machine-learning approach analyzing ~164,767 common SNPs for contamination and kinship scoring across DNA and RNA sequencing, referenced as context on how the field is extending CalculateContamination-style methods.
  3. Application of next-generation sequencing for detecting Mycoplasma contamination in veterinary vaccines — Source for the two-step 16S rRNA filter plus Mycoplasma database remap and de novo assembly approach used in the unmapped-reads check.
  4. Same-Species Contamination Detection with Variant Calling Information from Next Generation Sequencing — Source for using the inbreeding coefficient / excess heterozygosity as a same-species contamination signal, used in the heterozygosity check.

Part of the Contamination series.