1
Gotchas
Kemal Yaylali edited this page 2026-09-12 13:44:49 +01:00

Gotchas

Things that cost real debugging time here. Each one is a bug that was found and fixed; they are recorded because the symptom rarely points at the cause.

Science

A missing measurement is not a measurement of zero. rarity_score(None) read "this run has no allele frequencies" as "absent from gnomAD, therefore maximally rare" and handed every variant a free 0.25. The fix is structural: jobs record what they looked up, and a component with no evidence abstains. If you add a component, decide what makes it abstain first. See Ranking.

A DAG walked pre-order and read backwards loses ancestors. Computing each HPO term's ancestors with a pre-order DFS reversed looks like a post-order, but on a DAG a term can be reached before one of its parents, and then inherits that parent alone instead of the parent's whole lineage. It silently dropped 399 terms out of the phenotype branch — including Camptodactyly and Chiari malformation — so cases mentioning them scored lower for no visible reason. Now a true post-order, tested against a reference transitive closure.

A feature can cause its own label. The model took allele frequency as a feature, while ACMG's BA1/BS1 criteria assign ClinVar's benign labels using allele frequency. It was rediscovering the rule that generated its training data, and the impressive missense AUROC of 0.872 fell to exactly 0.500 when the feature was removed. See Benchmarks.

Split by gene, not by variant. A random split puts variants of the same gene on both sides and the model learns the gene. Grimm et al. documented this inflation for exactly this class of tool.

Pipeline

VEP's coordinates are not a primary key. Location and Allele trim indel alleles and shift positions; CT>C comes back as -> elsewhere. Identity travels in the VCF ID instead. See Pipeline.

--af_gnomade is rejected with --database, and plain --af silently returns nothing even for a variant at 15% global frequency. Frequencies genuinely need the cache — or a range request against gnomAD's public bucket, see Data sources.

Nextflow rejects two staged inputs sharing a filename. The cache placeholder and the plugin placeholder must be different files (assets/NO_CACHE and assets/NO_FILE), or the run fails with an input collision that names neither.

head plus pipefail kills a script silently. head closes the pipe, the upstream process takes SIGPIPE, and with pipefail the whole script aborts with no message.

Infrastructure

Kustomize only substitutes hashed ConfigMap names in workloads it considers in scope. Missing namespace: in an overlay means the hash is generated but never substituted, and pods mount a name that does not exist. The symptom is CreateContainerConfigError with correct-looking manifests.

A connection pool stops a container ever sleeping. Platforms that sleep idle containers measure outbound traffic; a pooled database connection is outbound traffic. See Deployments.

Local environment

macOS AirPlay Receiver owns port 5000 and answers 403, which looks exactly like an auth failure from MLflow. MLflow is on 5001 here.

*.rlwy.net may resolve to 0.0.0.0 on a machine with DNS filtering, so Railway's TCP proxy looks refused when it is fine. Check with a public resolver and connect by IP.

Security boundaries, not conveniences

vcf_uri validation is a boundary. A case's URI must be a gs:// object or an absolute path beneath LOCAL_DATA_ROOT, ending in a VCF suffix. Without it, a crafted value becomes a Nextflow option or reads an arbitrary file. It has its own tests; do not relax it for convenience.

The database URL never goes on a command line. Environment or Nextflow secret only, so it stays out of .command.sh and the workflow logs.