Troubleshooting¶
Common issues and solutions for gbcms. Issues are grouped by phase — work top-to-bottom for systematic diagnosis.
Installation Issues¶
Rust compilation fails: error: linker 'cc' not found
Install system build tools:
htslib linking error: cannot find -lhts
The Rust backend compiles htslib statically. Ensure compression library headers are present:
pip builds from source, or GLIBC_2.28 not found
The one prebuilt wheel (any CPython from 3.10, Linux x86_64) needs glibc 2.28 or newer (RHEL 8 and newer) and pip 20.3 or newer. With an older glibc or an older pip, pip builds the source distribution instead (Platform support).
On RHEL 8, install a Python from 3.10 on and use a virtual environment (python3.11 -m venv, then pip install --upgrade pip); see the
Legacy Linux install path. For glibc older than 2.28, use that page's micromamba + clangdev source install.
LIBCLANG_PATH not set error during build
Required for bindgen (htslib headers). Set:
clangdev (not clang) from conda-forge in your environment.
BAM / Input Issues¶
BAI index not found for sample.bam
Create or verify the index:
Chromosome X not found in reference / chromosome mismatch
gbcms looks up reference sequence by the chromosome name in the variant file. Ensure chromosome naming is consistent across VCF/MAF, BAM, and FASTA:
# Check names in FASTA
grep "^>" reference.fa | head -5
# Check names in BAM
samtools view -H sample.bam | grep "^@SQ" | head -5
# Check names in MAF/VCF
head -20 variants.maf | cut -f5 # Chromosome column
chr1 vs 1 and M/MT differences between the BAM and the variant file are
auto-reconciled — you do not need to pre-harmonize them. A contig that has no match
in the BAM is skipped with a one-time WARN (not a silent zero-count). The reference
FASTA, however, is fetched by the variant's contig name, so the FASTA must contain that
contig — otherwise the variant is rejected with verdict FAIL, reason FETCH_FAILED.
FETCH_FAILED reason (verdict FAIL) for all variants on a chromosome
This means the reference FASTA anchor could not be fetched for that contig. Because
BAM↔variant contig naming is auto-reconciled, this is now almost always a FASTA problem,
not a BAM one. Check:
- The contig exists in the FASTA under the variant's name (grep "^>" reference.fa)
- FASTA .fai index is present (samtools faidx reference.fa)
- No truncated / corrupted FASTA
Allele Count Issues¶
Start with the QC summary at the end of the run log: it counts the rows by what they need (fix the input, check the call, or nothing), names the first rows to look at and says what to do about each flag (End-of-run summary).
alt_count = 0 for a variant I can see reads for in IGV
Check in this order:
-
gbcms_statuscolumn — ifFAIL, the variant was excluded from counting;gbcms_status_reasonsays why (each reason). See Normalization Issues. -
Complex variants count exact carriers only — a delins, or a deletion whose anchor also changes (e.g.,
GC→T,AG→T), counts a read as ALT only when its bases carry the whole given allele with two flank bases each side (exact-carrier rule). Reads carrying a different allele there are neither (partial_altwhen closer to ALT), andgbcms_diagnosticnames the allele they carry (OBSERVED_ALLELE/COEXISTING_ALLELE). Use--traceto see each read's call:See Complex Indels for detailed case studies.gbcms dna --trace --variants variants.maf --bam sample.bam \ --fasta ref.fa --output-dir /tmp/debug/ 2>&1 | grep "read call" -
Left-alignment shift — for large deletions (≥5bp), BWA left-aligns the anchor; the CIGAR
Dposition may be several bases right of the anchor. Run normalization to see the left-aligned coordinates: -
BAM coverage — verify the BAM actually has reads at this position:
-
Verbose per-read trace — most diagnostic tool:
ref_count = 0 for a large deletion
A pure large deletion counts a clean read spanning its anchor as REF (check_deletion).
A large delins (a deletion with a replacement) is judged by the
exact-carrier rule: one pair
of equal-length windows read from the POS flank, so a REF read counts when it carries the
reference from that flank as far as the ALT read must carry the ALT.
If ref = 0, check:
- The variant was normalized correctly (gbcms_status is PASS)
- The BAM slice has coverage at the event's ends (samtools depth -a sample.bam -r CHROM:START-END)
- Reads actually reach a junction: reads mapping entirely inside a large deleted span,
or ending inside the event, carry no information about the variant and count as depth only
See NF2 Case Study.
alt_count is lower than expected for a deletion ≥5bp
BWA left-alignment can shift the anchor further left than where the CIGAR D appears.
S3 checks the haplotype a placement gives, so a deletion written elsewhere in its shift
region counts ALT; one written elsewhere with compensating mismatches counts ALT when the
read's bases spell the ALT across the window.
For deletions <5bp failing S3 sequence validation, CIGAR-definitive REF is used — this is intentional. A 1-4bp deletion in the wrong reference context is almost certainly spurious noise, not a left-alignment artifact.
To diagnose deletion ≥5bp still returning low alt, read the per-read calls: each
read call line names the rule that decided the read (rule=), and --verbose
adds a Phase stats line per variant:
Large Δalt vs sign-out t_alt for MNP / ONP / DNP variants
Several expected causes — not gbcms bugs:
| Cause | Pattern | Diagnosis |
|---|---|---|
| TRANS configuration | Two MNP alleles on opposite chromosomes; sign-out counts 2× reads | VAF ~2× expected |
| MAF annotation artifact | ONP defined at wrong position or missing adjacent SNV | gbcms normalize shows position shift |
| Sign-out duplication | Variant captured by two MAF entries (DEL+INS pair both counted) | Two nearby rows in MAF with same gene |
| Component carriers | Carriers hold only part of the annotated haplotype (e.g. one of two G>A changes): alt_count ≈ 0, partial_alt ≈ sign-out t_alt, PARTIAL_DOMINANT |
Correct for the annotated allele. --rescue-mnp reports the best-supported component; gbcms_rescue shows the per-position split |
Use --verbose to log per-variant read counts, or --trace for per-read detail.
dp = 0 for a variant
- BAM slice doesn't cover this sample:
samtools view -c sample.bam "chrom:pos-pos"returns 0 - BAM index missing: see BAI issue above
- Chromosome mismatch: see above
- Variant outside BAM region: the BAM file only covers certain genomic regions
DPF is much lower than DP (large gap)
The gap DPF − (RDF + ADF) does not mean data loss — it counts fragments discarded
due to R1/R2 disagreement within the quality threshold.
DPF - (RDF + ADF) = ambiguous fragments (both R1 and R2 covered but base quality within --fragment-qual-threshold)
A large gap at a specific locus is a quality signal indicating the site has many
discordant fragments — typical of noisy or error-prone positions. Adjust with
--fragment-qual-threshold (default 10).
ref_count + alt_count > total_count (INVARIANT violation)
This should never happen. If observed:
- Record the exact MAF row (
chrom,pos,ref,alt,Tumor_Sample_Barcode) - Run with
--traceto capture per-read classification - File a bug report at github.com/msk-access/gbcms/issues with the MAF row, BAM details, and trace log
The invariant ref_count + alt_count ≤ total_count is enforced throughout the counting engine.
Normalization Issues¶
REF_MISMATCH in gbcms_status — what does it mean?
The REF allele in the variant file does not match the reference FASTA at the stated position. gbcms requires ≥90% base match to auto-correct; below that, the variant is excluded.
Common causes:
- Wrong reference genome build (GRCh37 data with GRCh38 reference or vice versa)
- Chromosome naming mismatch (chr1 vs 1)
- Trailing-base error in MAF annotation (fixed by tolerant validation if ≥90% match)
- The REF written a few bases off (e.g. VCF alleles that keep the anchor base,
with Start advanced as if it were trimmed): gbcms_diagnostic then says
REF_AT_OFFSET(k), where the given REF matches exactly. The row is not moved
or counted; correct the input's coordinates.
Diagnose with:
WARN_REF_CORRECTED reason (verdict PASS) — is this a problem?
The REF allele was ≥90% match — corrected to FASTA sequence and counting proceeded normally.
The original MAF REF is preserved in the original_ref column (when --show-normalization is set).
This is not a problem in most cases. Common benign cause: 27bp EGFR exon 19 deletion with 26/27 bases correct and 1 trailing base that's a MAF annotation artifact.
If you want to audit all corrected variants:
WARN_HOMOPOLYMER_DECOMP reason (verdict PASS) — what changed?
Only files from before 6.7.0 hold it: --rescue-homopolymer is deprecated in 6.7.0 (removed in 6.8.0) and ignored. The run used --rescue-homopolymer, and the variant is a homopolymer run called as a
larger deletion (e.g. CCCCCC→T). In this sample, the corrected allele (the run with its
last base replaced, CCCCCC→CCCCCT) got more ALT support, so its counts were used.
used_decomposed = True in the output, and the flag is set per sample. Without the flag,
the row counts the given allele, and OBSERVED_ALLELE in gbcms_diagnostic names the
allele the reads carry.
It suggests the caller collapsed a smaller change at the run's end into one complex variant. The comparison is a heuristic: both alleles can claim reads carrying other forms of the run. Inspect the reads at the locus before relying on either allele's counts (see Variant Normalization).
After upgrading to 6.5.0, MAF rows from a VCF have different Start_Position / alleles
Since 6.5.0, VCF input is written to MAF exactly as vcf2maf writes it: the leading bases
REF and ALT share are trimmed, so e.g. TCT>TCG becomes the SNP T>G at the changed
base, and TTAC>A stays a 4bp deletion. The VCF record itself is kept in vcf_pos /
vcf_ref / vcf_alt; key on those to compare runs. Counts are unchanged. Re-genotype
every flavor with the same version before gbcms merge: MAFs from different versions do
not join. gbcms convert shows the conversion for a variant file without counting. See
Output Formats.
WARNING: Skipped VCF ALT ... — not countable
The VCF reader skips alleles that name no sequence to count: *, symbolic <...>
alleles, breakends, a missing ., other non-sequence alleles, and a REF that is not a
base sequence. The first few are logged individually, then one WARNING gives the totals
by reason. They get no output row. See Input Formats.
RNA Mode Issues¶
All counts are 0 in RNA mode
-
Strandedness filter with the wrong protocol — under the default
--strandedness reverse(dUTP), a forward-stranded or unstranded library's sense reads read as antisense and are excluded. Set--strandedness forwardorunstrandedto match the library. -
MAPQ filter too strict — default MAPQ=1 with NH:i:1 rescue is correct for STAR. If your aligner assigns MAPQ differently, check
--min-mapq. -
BAM not STAR-aligned — RNA mode assumes STAR BAMs with
NHtag. Other aligners may lack theNH:i:1tag needed for MAPQ rescue. Check:
rna_antisense_depth is 0 and every read counts as sense
The variant has no gene strand. gbcms resolves it from the --gtf exons at the
variant's position, or at an intronic position from the transcripts spanning it.
Without one, every read passes as sense, strandedness is not enforced, and a
run-level WARNING names such variants. Causes:
- no
--gtf, or a GTF that does not annotate the variant's contig; - an intergenic position (no transcript spans it);
- genes of both strands over the position (overlapping antisense genes): both genes' transcripts carry the allele, so every read counts by design.
Solutions:
- Pass --gtf with the annotation the BAM was aligned against.
- For an unstranded library, use --strandedness unstranded.
RNA editing flag (rna_editing_site) is always False
Ensure --rna-editing-db points to a valid REDIportal TABLE1 file:
# Verify file exists and is readable
head -3 TABLE1_hg38.txt.gz | zcat | head -3
# Download if missing
wget http://srv00.recas.ba.infn.it/atlas/download/TABLE1_hg38.txt.gz
Also verify chromosome naming matches (REDIportal uses chr-prefixed names).
Nextflow Issues¶
Container not found: Unable to find image 'ghcr.io/msk-access/gbcms:X.Y.Z'
Pull manually:
Task metrics error: Command 'ps' required by nextflow
Use a newer container image with procps installed. This occurs with minimal base images
that lack the ps command needed for Nextflow resource tracking.
When does gbcms exit non-zero? (orchestration / Nextflow)
gbcms exits 1 if any sample fails (a BAM raises during counting — e.g. a missing
index or a Rust panic surfaced as an error), whether all samples failed or only some.
Successful runs exit 0.
An empty or fully-rejected variant set is not a failure: a sample that legitimately
has no variants called (or whose variants are all rejected at prep) still exits 0, and
the rejected variants are written to the output with gbcms_status FAIL and the reason in gbcms_status_reason. This
lets a per-sample workflow keep going instead of failing the whole cohort on a sample that
genuinely has nothing to count.
Debugging Tools¶
gbcms has two levels of diagnostic output:
| Flag | Output | Use When |
|---|---|---|
--verbose |
Per-variant summary: read counts, classification decisions | First-pass diagnosis |
--trace |
Per-read detail: CIGAR walk, S1/S2/S3 checks, Phase 2/3 scores | Deep debugging a specific variant |
# Verbose: see per-variant decisions
gbcms dna --verbose --variants variants.maf --bam sample.bam \
--fasta ref.fa --output-dir /tmp/debug/ 2>&1 | tee debug.log
# Trace: per-read detail (slow — run on small BAMs or specific regions).
# Per-read trace lines print 0-BASED positions; variant-level lines are 1-based,
# so grep both spellings of the coordinate.
gbcms dna --trace --variants variants.maf --bam sample.bam \
--fasta ref.fa --output-dir /tmp/debug/ 2>&1 | grep -E "<pos0>|<pos1>" > trace.log
Inspecting Genomic Bin Construction¶
gbcms groups variants into ~10kb genomic bins before BAM traversal to reduce bam.fetch() calls (see Architecture → Genomic Binning). Bin construction is logged at INFO by default:
gbcms dna --variants variants.maf --bam sample.bam \
--fasta ref.fa --output-dir /tmp/out/ 2>&1 | grep -i "bin"
# Example (INFO):
# Built 42 genomic bins from 1247 variants (window=10000bp)
# Per-bin detail (read counts, filter breakdown) is DEBUG-level — add --verbose.
Expected bin counts
- Targeted panels (MSK-ACCESS, IMPACT): many variants per gene → very few bins per chromosome — optimal I/O
- Sparse WGS variants: ~1 variant per bin ≈ per-variant I/O (expected, not a bug)
- Unexpectedly many bins for a clustered MAF? Check for chromosome name mismatches (
chr1vs1) — the binner treats different chromosome names as separate bins regardless of coordinate proximity.
Related¶
- Installation — Setup guide
- Variant Normalization — Validation status reference
- Allele Classification — How variant types are classified
- Complex Indels — Complex Del+SNV, large DEL, shifted deletion case studies
- Nextflow Parameters — Resource configuration
abbreviations