You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Automatic thresholding silently accepts a sample that contains no usable modification-probability observations. The legacy path converts an empty probability map into a caller whose missing per-base thresholds default to zero. Modern QualHist consumers can likewise report successful empty results without distinguishing parsed records from actual probability observations.
In the legacy pileup-hemi reproduction, this makes an empty automatic sample scientifically indistinguishable from an explicit --no-filtering request. Other modern consumers can instead return a successful empty report without distinguishing an undefined estimate from an intentional explicit/no-filter mode. Fraction zero, empty inputs, and selector/filter combinations that sample no eligible calls can therefore disable filtering or falsely report successful automatic analysis.
Severity
Severity: High — scientific correctness and reporting
Rationale: An automatic threshold request can silently become threshold zero and emit results equivalent to no filtering. Users receive successful output without any estimated population, while explicit thresholds and true no-filter modes have materially different intent.
User and scientific impact
Affected legacy automatic-threshold consumers include duplex/entropy-style paths using the shared probability map.
Affected modern consumers include Summary, standard pileup, and extract calls paths using QualHist.
Empty samples can arise from fraction 0, empty modification tags, mapped/selector restrictions, or a valid sample containing records but no eligible probabilities.
The failure is easy to miss because the command may exit zero and produce plausible-looking no-filter output.
Affected versions and environment
Released version: modkit 0.6.4.
Development revision: 5cecc3fb3a9336068d9e3c68d5c08d678153dd2c.
Reproduced on macOS arm64 with checked-in duplex and empty-tag BAM fixtures.
Automatic thresholding requires at least one canonical or separated-modification probability observation. Record count alone is not evidence of an estimable distribution. Explicit --filter-threshold 0 and --no-filtering are the controls that may intentionally accept an empty population.
Observed behavior
Installed modkit 0.6.4 exits zero for both legacy commands. Each writes 341 rows, and both outputs have the same SHA-256:
The automatic Summary command also exits zero and reports:
mod_bases
total_reads_used 0
No automatic threshold was estimable in either case.
Expected behavior
Automatic mode must return nonzero and emit no scientific rows when the sampled population contains no usable modification probabilities. Paths that already resolve thresholds before writer creation must also preserve absent outputs and existing sentinels. Explicit thresholds and true no-filter modes must retain their documented empty-output behavior, and a sample containing only real modified-state probability histograms remains valid.
Root-cause evidence
The legacy threshold helper accepts an empty HashMap and constructs a caller whose absent per-base entries fall back to zero. The modern path does not consistently check whether canonical or separated-modification histograms contain observations before building thresholds or Summary results; an ok_records count can be nonzero even when the probability population is empty.
Proposed fix scope
Add one shared legacy guard before percentile/caller construction that rejects an empty probability map.
Add an explicit modern has_probability_observations predicate over canonical and separated-modification histograms.
Apply the guard only to automatic modes.
Preserve pre-output failure behavior on commands that already resolve automatic thresholds before writer creation; do not absorb unrelated writer-lifecycle changes.
Ensure entropy's documented no-filter mode truly bypasses sampling before the legacy guard is enabled there.
Non-goals
Fraction 0 remains a valid numeric fraction; it is the combination with automatic threshold estimation that cannot produce a threshold.
Do not reject explicit zero thresholds, --no-filtering, or valid all-modified samples.
Do not redefine malformed-record skip policy or selector eligibility.
Do not make a general transactional-output claim for later runtime failures.
Acceptance criteria
Fraction-zero and genuinely empty/filter-empty automatic samples fail with cannot calculate automatic thresholds because no modification probabilities were sampled.
The automatic failure emits no scientific rows. On paths whose setup is already bounded before writer creation, it neither creates a new output nor mutates an existing sentinel.
Summary, pileup, extract calls, duplex, and entropy automatic paths are covered across legacy and modern helpers.
Explicit threshold zero and no-filter controls succeed with their current empty outputs.
A sample containing only valid modified-state observations still computes a threshold.
BAM/CRAM and worker-count controls preserve the same outcome.
Standard pileup currently opens its writer before automatic threshold resolution; moving that writer is a separate setup/output-lifecycle repair and is not claimed here.
Summary
Automatic thresholding silently accepts a sample that contains no usable modification-probability observations. The legacy path converts an empty probability map into a caller whose missing per-base thresholds default to zero. Modern
QualHistconsumers can likewise report successful empty results without distinguishing parsed records from actual probability observations.In the legacy
pileup-hemireproduction, this makes an empty automatic sample scientifically indistinguishable from an explicit--no-filteringrequest. Other modern consumers can instead return a successful empty report without distinguishing an undefined estimate from an intentional explicit/no-filter mode. Fraction zero, empty inputs, and selector/filter combinations that sample no eligible calls can therefore disable filtering or falsely report successful automatic analysis.Severity
Severity: High — scientific correctness and reporting
Rationale: An automatic threshold request can silently become threshold zero and emit results equivalent to no filtering. Users receive successful output without any estimated population, while explicit thresholds and true no-filter modes have materially different intent.
User and scientific impact
QualHist.0, empty modification tags, mapped/selector restrictions, or a valid sample containing records but no eligible probabilities.Affected versions and environment
5cecc3fb3a9336068d9e3c68d5c08d678153dd2c.Steps to reproduce
Legacy empty sample:
Modern empty-observation sample:
Control or independent oracle
Automatic thresholding requires at least one canonical or separated-modification probability observation. Record count alone is not evidence of an estimable distribution. Explicit
--filter-threshold 0and--no-filteringare the controls that may intentionally accept an empty population.Observed behavior
Installed modkit 0.6.4 exits zero for both legacy commands. Each writes 341 rows, and both outputs have the same SHA-256:
The automatic Summary command also exits zero and reports:
No automatic threshold was estimable in either case.
Expected behavior
Automatic mode must return nonzero and emit no scientific rows when the sampled population contains no usable modification probabilities. Paths that already resolve thresholds before writer creation must also preserve absent outputs and existing sentinels. Explicit thresholds and true no-filter modes must retain their documented empty-output behavior, and a sample containing only real modified-state probability histograms remains valid.
Root-cause evidence
The legacy threshold helper accepts an empty
HashMapand constructs a caller whose absent per-base entries fall back to zero. The modern path does not consistently check whether canonical or separated-modification histograms contain observations before building thresholds or Summary results; anok_recordscount can be nonzero even when the probability population is empty.Proposed fix scope
has_probability_observationspredicate over canonical and separated-modification histograms.Non-goals
0remains a valid numeric fraction; it is the combination with automatic threshold estimation that cannot produce a threshold.--no-filtering, or valid all-modified samples.Acceptance criteria
cannot calculate automatic thresholds because no modification probabilities were sampled.Reproduction artifacts
tests/resources/duplex_modcalls_sort.bam21c2286ff7b2972a9c94ce24003bd3e20ba7c4fb38f416898a48eb5c2c789f94tests/resources/GRCh38_chr20.fa5806b504166c43762e11c809f720b28b6d50462104fea22e7490deffbe6b59cftests/resources/empty-tags.sorted.bam29ab421d7f924e681737904eaadd10bd9a392f4587a4dfb96ae175c39012d845tests/resources/empty-tags.sorted.bam.bai3696c4b4f0fe549db35b87e81e974967a727edfbf8f89595ed03faa29d9c1dd9Related work