Chatomics Field GuideWhat They Don't Teach You

Sanity check · Variant Calling

How to Catch a Sample Swap in Variant Calling

X heterozygosity, chrY coverage and pairwise genotype concordance take minutes to compute and catch mislabels that no alignment or variant-quality QC report will ever flag.

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

You joint-genotyped a cohort, the pipeline finished clean, no errors, no red text in any log. Then a tumor-normal pair doesn't share the germline background you expect, or an eQTL that should replicate across two assays from the same patients falls apart, or a longitudinal comparison between a patient's pre- and post-treatment sample looks like it's comparing two different people. Nothing crashed. Nothing warned you. That's what a sample swap looks like: silent, and indistinguishable from real biology until you go looking for it specifically.

Swaps happen at the bench (a mislabeled tube), at the sequencing core (an index/barcode mixup during multiplexing), or in the metadata spreadsheet (a transcription error linking sample ID to phenotype). They are not rare or exotic; they've been documented even in large, trusted consortia, and a single mislabeled sample from a CRO can quietly invalidate a whole analysis before anyone notices the figures look wrong for reasons that have nothing to do with your model.

This page gives you the checks to close that gap in under an hour: X heterozygosity and chrY coverage to verify sex against your sample sheet, and pairwise genotype concordance to verify identity across samples, timepoints, and assay types. None of it requires re-sequencing anything. All of it runs on the VCF and BAM files you already have.

What it looks like when it's happening

  • An X-heterozygosity plot shows a sample sitting between the male (~0) and female (~0.5) clusters instead of inside one of them.
  • A sample labeled 'female' in the metadata shows chrY coverage well above zero when you run samtools idxstats.
  • A tumor-normal pair that should share germline background shows genotype concordance no higher than two unrelated samples in bcftools gtcheck.
  • Two samples labeled as different, unrelated patients show near-perfect pairwise genotype concordance.
  • A CrosscheckFingerprints LOD score for a pair expected to match comes back strongly negative or hovering near zero.
  • A longitudinal pre/post-treatment comparison, or a multi-omic (RNA-seq vs DNA-seq) comparison for the 'same' patient, produces results that look like two different individuals.
  • PCA on common-variant genotypes across the cohort clusters two samples together that the sample sheet says belong to unrelated patients.

Why it happens

Sample identity errors happen upstream of every step your pipeline actually validates. A tube gets relabeled at the bench, a barcode gets assigned to the wrong sample on the sequencing core's worksheet, or a transcription error in the metadata spreadsheet links the correct sequencing data to the wrong patient ID. In none of these cases is the FASTQ file itself broken: the reads are real, they align cleanly, variants call normally, and every per-sample QC metric your pipeline reports looks fine. The only thing wrong is which label is attached to which genome, and no alignment or variant-quality metric can see that.

Multiplexed sequencing adds a second, purely technical failure mode: index hopping. On patterned flow cells, free adapter or index sequences can migrate between clusters during sequencing, causing a fraction of reads to be assigned to the wrong sample's demultiplexed FASTQ. Dual-indexed library prep reduces this substantially, but it doesn't reach zero, and it produces the exact same downstream signature as a bench mixup: clean-looking data attached to the wrong identity.

X heterozygosity works as an identity check because male and female genomes are structurally different on chrX in a way that's independent of tissue, treatment, or timepoint. XY individuals are hemizygous for chrX, so a true heterozygous genotype call there is almost always a technical artifact of mapping (often from pseudoautosomal regions or paralogous sequence), keeping Xhet near zero. XX individuals are diploid on chrX and carry real heterozygous SNPs across it, pushing Xhet toward 0.5. That gives you two well-separated, biologically-grounded clusters to check every sample against its recorded sex.

Genotype concordance works for the same reason: germline genotype at common SNPs is a fixed property of an individual's genome. It doesn't change between tissues, between an RNA-seq library and a WGS library, or between a sample drawn today and one drawn six months ago. If two samples that are supposed to come from the same person show low concordance at those sites, or two samples supposed to come from different people show high concordance, the genotype is telling you the labels are wrong, independent of anything else in the pipeline.

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. Pull chrX biallelic SNV genotypes for every sample from the joint-called VCF and calculate the fraction that are heterozygous. This is the single cheapest identity check you can run because it needs nothing but the VCF you already have.

    bash
    bcftools view -r chrX -m2 -M2 -v snps cohort.vcf.gz | \
      bcftools query -f '[%SAMPLE\t%GT\n]' - | \
      awk '{tot[$1]++; if($2=="0/1"||$2=="0|1") het[$1]++}
           END{for (s in tot) print s, het[s]/tot[s]}'
    Healthy
    Two tight clusters: samples labeled female sit around Xhet ~0.5 (they carry two X alleles and are truly heterozygous at many sites); samples labeled male sit near Xhet ~0 (hemizygous, so an apparent het call on chrX is a mapping artifact, not biology).
    Red flag
    A sample sits between the clusters (e.g. Xhet 0.15-0.35) with no matching intermediate karyotype in the chart, or a sample's Xhet cluster contradicts the sex recorded on its label.
  2. Run samtools idxstats on each BAM/CRAM and compute mapped-read counts on chrY normalized by chromosome length, then compare to the same normalized value for chrX.

    bash
    samtools idxstats sample.bam | awk '$1=="chrY"{y=$3/($2+1)} $1=="chrX"{x=$3/($2+1)} END{print y, x, y/x}'
    Healthy
    Genetic males show substantial chrY coverage and roughly half the chrX coverage of a female; genetic females show near-zero chrY coverage and full chrX coverage. Xhet and chrY coverage should agree on the same sex call for every sample.
    Red flag
    chrY coverage says male while Xhet says female (or vice versa) for the same sample, or chrY coverage is intermediate, neither clearly present nor clearly absent.
  3. Join your Xhet/chrY sex calls back onto the metadata table by sample ID and flag every row where inferred sex and recorded sex disagree. Do this as a script, not by eye, so it reruns automatically every time a new batch lands.

    Healthy
    100% agreement between inferred and recorded sex across the cohort, matching what a validated method should give you (XhetRel-based sex clustering has shown complete concordance with orthogonal calls like DRAGEN ploidy and SNP-array sex in published benchmarking).
    Red flag
    Any mismatch. One mismatched sample out of 200 is exactly the kind of quiet, non-crashing failure that a swap produces; it will not throw an error anywhere in your pipeline.
  4. Use bcftools gtcheck to compare genotypes between every pair of samples (or a query sample against a reference set) at a common panel of variant sites. This is your primary swap detector for samples that should or shouldn't match a known identity.

    bash
    bcftools gtcheck -e 0 cohort.vcf.gz > gtcheck.txt
    # or against a known reference set:
    bcftools gtcheck -g reference.vcf.gz query.vcf.gz
    Healthy
    Samples that are supposed to be the same individual (tumor-normal pair, replicate, longitudinal timepoints) show high concordance; unrelated individuals show low, roughly background-level concordance.
    Red flag
    A tumor-normal pair or a pre/post-treatment pair from the same patient shows discordance close to what unrelated samples show, or two samples labeled as different patients show near-perfect concordance.
  5. When you have a specific pair you're worried about (a CRO delivery, a suspicious replicate), run bcftools stats with both VCFs and extract the Genotype Concordance by Sample for SNPs (GCsS) line for a direct per-sample number instead of scanning the whole gtcheck matrix.

    bash
    bcftools stats -s - sampleA.vcf.gz sampleB.vcf.gz | grep '^GCsS'
    Healthy
    A GCsS value consistent with what the two samples are supposed to be: near-identity for a duplicate/technical replicate, expected background concordance for genuinely unrelated samples.
    Red flag
    GCsS for a pair labeled as the same individual sits at background level, or a pair labeled unrelated sits far above background.
  6. For a BAM/CRAM-level check independent of your variant-calling filters, run CrosscheckFingerprints with a haplotype map of common SNPs and inspect the LOD score for every pair. GATK does not publish one universal default LOD cutoff, so read the LOD column directly rather than trusting a single hard threshold: strongly positive means same individual, strongly negative means different individuals, values near zero mean you don't have enough informative markers to call it either way.

    bash
    gatk CrosscheckFingerprints \
      --INPUT sample1.bam --INPUT sample2.bam \
      --HAPLOTYPE_MAP fingerprint_hapmap.vcf \
      --OUTPUT crosscheck.txt \
      --LOD_THRESHOLD -5
    Healthy
    Pairs expected to be the same individual give a strongly positive LOD; pairs expected to be different individuals give a strongly negative LOD.
    Red flag
    A pair expected to match gives a strongly negative LOD, or a near-zero LOD that you're tempted to round off to a confident call without adding more markers or depth first.
  7. If you have RNA-seq, ATAC-seq, or ChIP-seq alongside DNA-seq for the same patients, call SNPs from the expressed or accessible regions of the other assay (heterozygous common SNPs are recoverable cheaply from RNA-seq reads) and run the same gtcheck/GCsS comparison against the DNA-seq VCF. This is the most involved check because it means running a variant caller on data you didn't originally plan to genotype, but it's the only check that catches a swap between assays rather than within one.

    Healthy
    Genotype concordance between the DNA-seq and RNA-seq/ATAC-seq calls for the same patient is high at the common SNP sites both assays can see.
    Red flag
    The RNA-seq-derived genotype for 'patient A' doesn't match the DNA-seq genotype on file for patient A. That mismatch is exactly the pattern a CRO mislabel produces: same barcode, wrong tissue, everything else in the pipeline runs clean.

What to do about it

Confirmed swap between two specific samples

When: Xhet/chrY sex mismatch and genotype concordance both point to the same two samples being switched relative to their labels.

Trace the swap back to source: check the sequencing core's sample sheet, the library prep worksheet, and the original tube labels before touching any files. Relabel at the metadata level, not by silently renaming VCF sample columns, then rerun every downstream analysis that used either sample and reverify identity again after relabeling.

Caveat: If you can't find documentary evidence of which sample is which, a genotype match alone can tell you two samples are swapped relative to each other but not which one is correctly labeled. You may need a second identity anchor (a known SNP genotype, a prior confirmed timepoint) to resolve direction.

Ambiguous Xhet clustering (neither clearly male nor clearly female)

When: A sample sits between the two Xhet clusters and chrY coverage doesn't clearly resolve it either.

Check depth and missingness on chrX for that sample first; low coverage alone can pull Xhet toward 0.25-0.35. If depth is adequate, treat it as a real possibility of contamination, sample mixing, or an atypical sex chromosome constitution (XXY, XO, mosaicism) and escalate to the wet lab or clinical team rather than forcing a sex label.

Caveat: Don't quietly relabel a patient's sex based on Xhet alone. If this is a clinical sample, atypical sex chromosome findings can be a real, reportable result, not a QC artifact to paper over.

Low pairwise concordance between a known tumor-normal pair

When: gtcheck or GCsS shows discordance for a pair that should share germline background, but the discordance is moderate rather than at background level.

Check depth and missingness at the compared sites before concluding it's a swap; degraded or low-input samples (FFPE, low-cellularity biopsies) genotype poorly and can drag concordance down even for a genuine match. Rerun the comparison restricted to high-depth, high-confidence sites only.

Caveat: Loosening filters to force a match is backwards. If concordance stays low after restricting to well-covered sites, trust the number, not the label.

Borderline LOD score from CrosscheckFingerprints

When: A pair's LOD score sits near zero instead of strongly positive or negative.

Increase the number of informative markers in the haplotype map, or pull more depth at those sites, before treating the result as inconclusive. A near-zero LOD means you don't have enough evidence yet, not that the samples are unrelated.

Caveat: Tumor-aware CrosscheckFingerprints runs roughly double the compute time; only turn it on when you specifically need to detect somatic contamination, not as a default setting.

Swap discovered after downstream results already exist

When: You find a mislabel after DE, GWAS, or clinical interpretation has already used the affected samples.

Quarantine the affected samples immediately, notify collaborators and anyone who has seen the downstream results before it goes further, and rerun the affected analyses excluding those samples until they're correctly relabeled and reverified with a second independent check.

Caveat: This is the expensive path. It's exactly what the checks in this guide are meant to prevent by running before, not after, you build conclusions on top of the data.

When not to "fix" it

Don't force a relabel when the "mismatch" is biology. Related individuals (parents, siblings, twins) in a family or trio study will show elevated genotype concordance and relatedness by design; that's the expected signal, not a swap, and a single hard concordance threshold will misclassify it. A patient with a real atypical sex chromosome constitution (XXY, XO/Turner mosaicism) will show intermediate Xhet or discordant chrY coverage that has nothing to do with mislabeling; escalate to the clinical or wet-lab team instead of quietly correcting the sex field. Tumor samples with extensive loss of heterozygosity on the X chromosome can also show artificially male-like Xhet even from a female patient; check the tumor purity and copy-number state before treating that as a sex-check failure. And technical replicates or same-patient multi-timepoint samples are supposed to look nearly identical; treat a match as confirmation of correct labeling, not evidence of a processing error.

Five things experienced analysts do here

  1. Run the sex check and a cohort-wide gtcheck as an automated step in the pipeline, producing a table of inferred-vs-recorded sex and a concordance matrix, not something you eyeball once and forget.
  2. Keep a version-controlled haplotype map file for CrosscheckFingerprints across projects so every new batch of samples gets checked against the same panel without re-deriving it each time.
  3. Record known relatedness (family structure, technical replicates, same-patient multi-omic pairs) in your metadata up front, so a relatedness check doesn't flag expected partial concordance as a false swap.
  4. When you find one confirmed swap, audit the rest of that batch or that core delivery immediately; mislabels rarely happen in isolation, and the CRO or core that swapped one tube may have swapped more.
  5. Treat identity verification as mandatory before any cross-timepoint or cross-assay comparison (multi-omic integration, pre/post-treatment), not as an optional step you add only after something looks wrong.

Questions people ask

What's the fastest way to check if two samples from different assays (RNA-seq and DNA-seq) come from the same patient?

Call SNPs from both assays at a shared panel of common, well-covered biallelic sites and run bcftools gtcheck or extract the GCsS metric from bcftools stats. RNA-seq only sees expressed loci, but that's enough for common SNPs in moderately-to-highly expressed genes, and it costs you a variant-calling pass, not a new experiment.

My sample's Xhet is 0.25, not 0 or 0.5. Is that a swap?

Not necessarily. Intermediate Xhet can come from low depth, contamination, FFPE degradation, or a genuine atypical sex chromosome constitution like XXY or mosaicism. Check chrY coverage jointly, rerun with the standard XhetRel filtering (allele fraction >0.25, depth >20, ≤20% missing genotypes) before you call it a swap.

Will genotype concordance flag related individuals as false swaps?

Yes, if you only look at a single pass/fail threshold. Parents, siblings and other relatives share more alleles than unrelated people but less than a true identity match. Use a relatedness estimate, not a binary concordance cutoff, and keep known family structure in your metadata so expected relatedness doesn't trigger a false alarm.

Does this catch index hopping on the sequencer, not just bench mixups?

Yes. These checks compare the genotype actually present in the reads against the identity the sample is supposed to have, regardless of how the mislabeling happened, whether that's a tube swap at the bench, a transcription error in the metadata sheet, or index hopping between adjacent libraries on a patterned flow cell.

Do sex checks and genotype concordance work on RNA-seq, or only WGS/WES?

They work across data types. Xhet-style sex clustering and tools like NGSCheckMate are built to compare allele fractions of known SNPs across WGS, WES, RNA-seq and ChIP-seq, so you can cross-check identity between assay types run on the same patient, not just within a single DNA-seq run.

Related pages

Related reading on the blog

Sources

  1. XhetRel: A Pipeline for X Heterozygosity and Relatedness Analysis of Sequencing Data — Xhet clustering by sex, standard filtering parameters, and validation against DRAGEN ploidy and SNP-array sex calls
  2. bcftools GitHub Issue: genotype concordance and dosage r-squared in bcftools stats — gtcheck and the GCsS metric for per-sample genotype concordance
  3. CrosscheckFingerprints (Picard), GATK — LOD score interpretation and tumor-aware mode runtime cost
  4. Detecting sample swaps with Picard tools, GATK — LOD-based fingerprinting workflow for flagging mismatches

Part of the Sample swaps series.