Errors
Every failure mode has its own type, so a caller can tell a reference mismatch from a naming mismatch without parsing English. This page is organised by what you saw; the API reference has the class details.
Start here
| What you saw | What it is | Go to |
|---|---|---|
ContigNamingError | chr1 against 1 | Naming |
ContigOrderError | Same contigs, different order | Order |
ReferenceFrameError | The reference is not the genome a step’s coordinates mean | Frame |
DimensionError | Two inputs disagree about something | Dimensions |
UndeclaredDimensionError | Something must be declared and was not | Undeclared |
StepTypeError | A step was handed the wrong kind of data | Types |
StepDefinitionError | The step itself cannot be used as one | Definition |
LoadError | A file would not read | Loading |
RunError | A step failed while running | Running |
A noticed line, not an error | A tool named a sample after a file | Notes |
check passed but results look wrong | Not a Ligase error | When check is not enough |
How they relate
Everything found before anything runs is a CheckError. That is the
useful distinction: a CheckError cost you milliseconds, anything else cost
you a run.
Exception
├── CheckError found by `check`; nothing has run
│ ├── DimensionError two inputs disagree
│ │ ├── ContigNamingError ... about naming style only
│ │ ├── ContigOrderError ... about contig order only
│ │ ├── ReferenceFrameError ... about which genome the numbers mean
│ │ └── UndeclaredDimensionError
│ └── StepTypeError wrong kind of data for the step
├── LoadError a file would not read
└── RunError a step failed while running
TypeError
└── StepDefinitionError the decorated function is not usableCatching CheckError catches all six of the first group. Catching
DimensionError catches the naming and order cases too, which is usually
what you want; catch ContigNamingError on its own only when you mean to
treat naming differently from everything else.
import ligase as lg
try:
pipeline.check()
except lg.ContigNamingError as e:
# Specifically a naming mismatch: a renaming step would fix it.
print(e.step, e.dimension)
except lg.CheckError as e:
# Anything else found before compute. `e.errors` has all of them.
for problem in e.errors:
print(problem)ContigNamingError
The same contigs under different names — chr1 against 1. The engine
has checked that the contigs are otherwise identical, which is why this is
its own type rather than a reference mismatch.
Fix it by using one naming convention throughout, or by inserting a renaming step. Full worked explanation, with the real diagnostic: my BED file intersects nothing.
ContigOrderError
The same contigs, in a different order. htslib matches contigs by name and does not care; GATK requires matching sequence dictionary order and will refuse. Reported because silence would hide a failure in a tool you may add tomorrow.
GATK rejects my sequence dictionary.
ReferenceFrameError
The reference is not the genome a step’s own coordinates are counted
along. Most dimension errors are two inputs disagreeing; this one is an
input disagreeing with the engine. lg.hivdb.interpret holds the HIV-1 gene
windows as positions in HXB2 K03455 counted from its first base, and a
reference numbered any other way puts a different gene under each window.
The case that matters is a pol-relative reference — K03455 2085–5096, so every position 2084 lower. Protease’s window then lands in integrase, and before this error existed the report came back with protease-inhibitor resistance at a clinical level.
The fix is not a different file. It is the same sequence put back where its own numbers mean what the windows say:
ref = lg.reference(
"hxb2_padded.fasta", # "N" * 2084 + hxb2_pol
organism="HIV-1",
frame=("K03455", 1),
)A whole-genome HXB2 reference needs no declaration — 9,719 bases identifies it — so you see this only when the reference is something else. Why the frame is declared rather than inferred: D33. The worked account, and what it cost before it was an error: my resistance report names the wrong gene.
DimensionError
Two inputs to a step disagree about a dimension. The base case, raised when the disagreement is not specifically naming or order — a different reference build, a different coordinate convention, a different sample set.
The message names both values, where each came from, and asks a leading
question. The provenance is the actionable part: knowing two references
disagree sends you hunting, while knowing one came from a BAM @SQ header
and the other from the FASTA you passed tells you what to change.
- My variant positions are wrong — reference build
- My intervals are off by one — coordinates
- My sample labels look swapped — sample set
UndeclaredDimensionError
A dimension some step requires is unknown, and must be declared. Not every dimension can be read out of a file: no FASTA records its organism, and a BAM header does not say which build it was made against.
Ask what is missing and why:
>>> aln.unknown_reason("organism")
'not recorded in SAM/BAM headers; declare it'Then declare it once, at load:
aln = lg.alignment("data/alignments/sample01.bam", organism="human")A dimension that no downstream step requires may stay unknown. See declaring dimensions.
StepTypeError
A step was handed a kind of data it does not declare. A Variants where
the signature says Alignment. Blames no dimension — dimension is None
on this one — because the problem is the type, not a value within it.
Usually an argument in the wrong position, or a step whose return annotation does not match what the next step takes.
StepDefinitionError
The decorated function cannot be used as a step, raised at definition time rather than at check time. A parameter with no annotation, a missing return annotation, or an annotation the graph cannot interpret.
This one subclasses TypeError, not CheckError, because it is a mistake in
the code rather than in the data.
LoadError
A file could not be read, or did not parse as the format expected. The
path is wrong, the file is truncated, or a .fasta is not a FASTA.
Raised by every loader — lg.reference, lg.alignment, lg.variants,
lg.reads, lg.intervals, lg.entity_set, lg.ranked_list, lg.samples
and lg.remote — at the point of loading, so it names the file.
It is also what a molecule table gets when its own keys refute the
standardisation it claims. cannot read <file> in the state it says it is in
means the table is declared charge-neutralised — by an argument, or by the
representation line in the file — and at least one of its InChIKeys is not:
the third block of a key is its protonation layer, and N is the only value
it takes for a molecule with no proton added or removed. The message names
the key, the row and the state those keys are in. Nothing else about a
representation is checked, because nothing else about one leaves a trace in a
key.
RunError
A step failed while running. Carries a report with the whole run’s
outcome and a failures list of StepFailure, each naming its step, its node
and the sample it stood for.
This is the one class of error that cost you compute. Everything the cache had already is still there, so fixing the step and running again does not redo the work above it.
try:
pipeline.run()
except lg.RunError as e:
for failure in e.report.failures:
print(failure.step, failure.sample, failure.message)When check is not enough
check compares dimensions. It cannot tell you that your aligner’s settings
were wrong, that your reads are contaminated, or that a threshold is
inappropriate for your question. A pipeline that checks clean can still be
scientifically wrong.
Two things help with the part Ligase can speak to:
ligase audit <step>runs a step twice and reports whether it repeats. Non-determinism that the cache would hide shows up here. See semantic hashing and auditing.ligase methods --verifychecks a reproducibility bundle against what this machine produces, which catches an environment that has drifted.
And a noticed line in a run report is neither an error nor nothing: it is
the engine telling you something about your data it could not refuse over.
The common one is a tool naming a sample after a file, which happens when an
alignment carries no @RG SM tag — see
reading what the engine noticed.