Skip to content

Troubleshooting

This page covers these problems: an automatic truncation length that throws away every read, a PhiX-removal step that stops on the read pairs, a shotgun OTU table that comes out empty at a rank other than species, a Bracken rank that MetaFlux refuses, a shotgun table step that stops on the codes of the Kraken2 reports, a keep/discard token refused at start-up, a shotgun taxon filter that removes every taxon, small differences between two runs on the same data, a DADA2 error model that never converges, an outdated marker pack in config/markers/, a reference file that fails the reference check, a reference download that fails its md5 or SHA-256 check, and YAML mistakes in the config. Each entry gives the symptom, the reason behind it, and what to change.

Amplicon: automatic truncLen can drop all reads on very clean runs

Symptom

The run gets through primer trimming and QC, then stops at the DADA2 filtering step (rule dada_filter) with:

Error: No reads passed the filter ...

Cause. With amplicon.trunc_len.mode: auto, the pick_trunclen rule reads the per-base quality tables that Falco wrote for the primer-trimmed reads (stats/falco/{sample}_R{1,2}_stripped/fastqc_data.txt) and cuts just before the first position whose per-base lower quartile (Q1, the 25th percentile of quality at that cycle) falls below amplicon.trunc_len.q_threshold — 20 by default — so the cut keeps the last cycle still at or above it. If quality never drops that far, and clean MiSeq runs often stay high to the last cycle, truncLen lands on the ceiling instead.

Falco reports a quality bin for every position up to the longest read in a file, however few reads reach it — so a ceiling taken from the quality table alone describes the longest reads, not typical ones. On a library where a few reads run to full length and most stop a little short, a cut placed there discards nearly everything.

MetaFlux guards against this by default: amplicon.trunc_len.min_read_coverage_pct (95) caps each sample at the length that percentage of its reads still reach, before quality is considered at all. The guard is on by default, so if this error appears anyway, the cause is one of the two cases below.

After primer trimming most reads end a little before the full read length, and DADA2's filterAndTrim discards any read shorter than truncLen rather than padding it. So a cut placed at the very end of the read removes almost everything, and DADA2 has nothing left to work with.

What it looks like on a real library without the guard

The 18S test dataset (PRJDB12231; see test datasets) shows it. Its R1 quality never falls below Q20, so without the guard (MetaFlux v2.1.0 and earlier) auto puts the R1 cut on the ceiling and returns truncLen = 288 / 239. After primer trimming the reads themselves are:

min p05 median max
R1 78 271 281 289
R2 54 282 282 298

The ceiling comes out at 288 because the strictest sample sets it: its longest R1 read is 288 bp, while the other three reach 289. Most R1 reads (70%) stop at exactly 281. Since filterAndTrim discards anything shorter than truncLen, only 9 of 908,768 pairs are long enough for the cut, and without the cap the run finished with 3 ASVs.

Retention against the R1 cut, measured on this library:

truncLen R1 288 281 275 270
pairs retained 0.0% 70.3% 92.4% 100.0%

Moving the R1 cut from 288 to 270 bp raises retention from 0% to 100%.

With the 95% read-coverage cap the R1 ceiling comes in at 271 instead of 288, and auto returns 271 / 239 on this library with no manual intervention. It carries 90.9% of the raw reads through to the final table, practically the same as a hand-picked 270 / 239.

Why it can still happen with the guard on. Two cases:

  1. min_read_coverage_pct was lowered. The value is a guarantee about reads, so a lower percentage permits a longer cut — that is the direction that reintroduces this failure, and it is the first thing to check. Raising it does the opposite, and 100 is not an off switch: it collapses the ceiling onto the single shortest read in the file, which fails the overlap constraint instead.
  2. Read lengths are ragged enough that even the 95% length is unrepresentative. Heavily pre-trimmed input does this, as do markers whose amplicons genuinely vary in length. logs/pick_trunclen.log tells you directly — it prints each sample's longest read alongside the length 95% of its reads reach, and emits a NOTE when the gap is 10 bp or more. A very large gap there means the ceiling is being set from a distribution with a long tail, and a hand-picked value will beat it.

Fix — set the truncation length by hand.

  1. Look at the read lengths after primer trimming: the QC | primer-trimmed reads | {sample}_R{1,2} entries under "Sequence Length Distribution" in the MultiQC report, or the per-sample Falco reports under stats/falco/{sample}_R{1,2}_stripped/.
  2. Switch to mode: manual and set manual_r1 / manual_r2 a little below the length most reads still reach, so filterAndTrim stops discarding them.
  3. Keep the pairs long enough to still merge: truncLen_R1 + truncLen_R2 ≥ amplicon_length + min_overlap (amplicon.min_overlap is 12 bp by default).
amplicon:
  trunc_len:
    mode: manual
    manual_r1: 270
    manual_r2: 239

Pick the R1 and R2 cuts separately

Only the read whose quality stayed flat is stuck on the ceiling. In the example above R2 did degrade, so auto found a quality-based cut for it at 239. Only R1 needed a new value; R2 kept the cut that auto found.

Retrying is cheap

Snakemake picks up where the run left off: PhiX removal, primer trimming and QC are already done and stay done, so only DADA2 and the steps after it run again. Trying a second value costs minutes, not a full re-run.

ITS is not affected

For type: ITS, dada_filter overrides truncLen to c(0, 0) unconditionally — ITS amplicons vary in length, so a fixed cut would throw away genuinely short sequences. The 3′ tail is still cleaned, by truncQ and maxEE instead. Because the override is unconditional, manual_r1 / manual_r2 have no effect on an ITS run either.

Background on how the cut is chosen, and on the overlap constraint, is in Amplicon length and truncation.

Shotgun: BBDuk PhiX removal stops on the read pairs

Symptom

The shotgun run aborts at rule decontam_phix with one of these two messages:

java.lang.AssertionError:
There appear to be different numbers of reads in the paired input files.
The pairing may have been corrupted by an upstream process.  It may be fixable by running repair.sh.
java.lang.AssertionError: List size mismatch: 600 vs 601

The first means the pairing is broken. The second does not: it comes from a newer bbmap than the one MetaFlux pins (see the end of this entry).

Cause. BBDuk reads the R1 and R2 files in lockstep, one record from each at a time, and requires both files to hold the same number of records in the same order. Read length has nothing to do with it — ragged lengths, and mates of the same pair ending up different lengths, are processed normally. "There appear to be different numbers of reads" means R1 and R2 contain different numbers of reads, which usually happens when reads were filtered before deposition and orphaned mates were left behind in one file. SRA and BioProject submissions are the common case. MetaFlux's own fetch can produce it as well: rule download_sra calls fasterq-dump --split-files, which writes a record to _1 only for any spot holding a single read (--split-3 is the option that keeps the two files in step).

The BBMap host-removal step (rule decontam_host) fails on the same input, for the same reason and with the same message: BBMap reads the pair in lockstep too and makes the same requirement. The message names the fix outright.

Fix — repair the pairing. repair.sh ships in the same bbmap conda package MetaFlux already installs for these rules, so no extra tool is needed:

repair.sh in1=R1.fastq.gz in2=R2.fastq.gz \
  out1=fixed_R1.fastq.gz out2=fixed_R2.fastq.gz \
  outs=singletons.fastq.gz

It re-syncs the two files on read name and writes the leftover single-ended reads to outs=. Point input.fastq_dir at the repaired files and run again. Where the raw, untrimmed reads are still available they are the better starting point: they are properly paired, and MetaFlux trims with fastp anyway, so pre-trimmed input buys nothing.

remove_phix: false is not a fix here

Switching PhiX removal off only moves the failure to BBMap, which stops on the same files. Switching host removal off as well moves it to fastp, which stops on unequal record counts too. Repair the files instead.

List size mismatch is a different problem. MetaFlux pins bbmap 39.52, whose BBDuk and BBMap never print this message. It comes from the rewritten BBDuk that bbduk.sh runs in bbmap 39.76 to 40.02: its paired-read reader loses track of mates and stops with this message, or hangs, on correctly paired reads, above all on trimmed reads whose mates differ in length. It prints the same message on broken pairs too, so it says nothing about the pairing, and repair.sh does not help. The first lines of logs/decontam_phix/{sample}.log name the version that ran (Version 39.52 for the pinned one).

Fix — run with MetaFlux's environment. Start the run with --sdm conda, so that Snakemake installs and uses the pinned environment (workflow/envs/bbtools.yaml), and keep the pin if you edit that file. MetaFlux before v2.5.0 did not pin bbmap, so its environment could get one of these releases.

More on host references and masking is in Decontamination.

Shotgun: empty or nearly empty OTU table with tax_lev other than S

Symptom

shotgun.bracken.tax_lev is set to G, or to any rank other than S. The run finishes without an error, but 03.abundance/otu_table.tsv has no rows, or only a handful, while Bracken's own {sample}_output.txt files list plenty of taxa.

Cause. Fixed in v2.5.0. Up to v2.4.0, rule kraken_biom ran kraken-biom with its default settings, which assume a species-level report: it takes the clade count for species, and from genus up to order only the reads placed directly on the taxon. Plain Bracken 3.1 writes 0 in that column for almost every taxon above species (a bug in Bracken itself, fixed in the 3.1p1 build that MetaFlux pins from v2.5.0 on), so those taxa dropped out. A genus survived only when Kraken2 had placed no reads on any of its species, and at class, phylum or domain level, which lie outside that range, nothing survived at all. Bracken's {sample}_output.txt files were never affected, and neither were species-level runs.

Fix. Update to v2.5.0 or later and run the same command again, from the same MetaFlux folder. From v2.5.0 on, MetaFlux builds the table with rule finalize_otu_table straight from the Bracken reports, from their clade counts, and pins Bracken 3.1p1. In a folder made with an earlier version every step runs again, Kraken2 included, because the output folders have new names (see What happens on a re-run), so nothing has to be forced.

A config with tax_lev: D stops at start-up from v2.5.0 on; see the next entry.

Shotgun: the run refuses tax_lev: D or K

Symptom

A run with Kraken2 (profiler: kraken2 or both) stops before any step starts, with one of these messages (wrapped here):

[MetaFlux] shotgun.bracken.tax_lev: D is not allowed. Viruses have no domain in
NCBI's taxonomy (since 2025), and at this rank Bracken adds their reads, with a
share of the reads left at the root, to the domain the Kraken2 report lists last
(usually Archaea). Use P or a lower rank. To compare domains, add up the rows of
otu_table.tsv by their d__ slot; that sum counts the reads Bracken placed at the
table's rank, not every read of the domain.
[MetaFlux] shotgun.bracken.tax_lev: K is not allowed. Most protists have no
kingdom, and a GTDB database has no kingdom rank at all. At this rank Bracken adds
the reads of a taxon without a kingdom to the kingdom the Kraken2 report lists just
before it, and on a GTDB database it stops after Kraken2. Use P or a lower rank;
the k__ slot of otu_table.tsv gives the kingdom of each row.

Cause. Bracken reads a Kraken2 report from top to bottom and adds the reads of every row below the target rank to the last row of that rank it has read. For a taxon that has the target rank, that last row is the taxon's own ancestor, because the report lists every branch right after its parent. A taxon without the rank is counted towards whichever row of that rank comes before it. At domain and kingdom level whole groups of taxa lack the rank:

  • Domain. Viruses have no domain in NCBI's taxonomy since 2025, and their branch comes after all cellular organisms. Their reads, and the share of the root's reads that Bracken attributes to them, go to the domain listed last. Kraken2 lists sister rows by read count, largest first, so this is the domain with the fewest reads, usually Archaea. In a saltmarsh rhizosphere sample (SRR6394731, BioProject PRJNA422917) classified with the Kraken2 index k2_NCBI_reference_20251007, Archaea had 2,612 reads at phylum level and 35,636 at domain level, from only 71 viral reads and the root reads attributed to them.
  • Kingdom. Most protists have no kingdom in NCBI's taxonomy, and GTDB has no kingdom rank at all. The protists' reads go to the kingdom listed just before them: in the same rhizosphere sample, Fungi had 7,957 reads in its phyla but 12,001 at kingdom level. On a GTDB database Bracken finds no kingdom and stops with "no reads found", after Kraken2 has run.

Fix. Set tax_lev to S, the default. It is the only rank where this cannot happen, because every row below a species row is a strain of that species. At P to G it hits only the few taxa that lack that rank, but one row can still gain several times its own reads: in the rhizosphere sample, protists without a phylum took Oomycota from 56 to 1,095 reads at P, and bacteria without an order took Nevskiales from 1,393 to 6,237 at O. At these ranks, compare a surprising row with the species-level table.

Domain and kingdom totals can be read from any of these tables: add up the rows of otu_table.tsv by their d__ or k__ slot. That sum counts the reads Bracken placed at the table's rank, not every read of the domain or kingdom: reads that Kraken2 left above that rank, and that Bracken could not push down, are in no row (see Bracken abundance re-estimation). With profiler: sylph Bracken does not run, and tax_lev is not checked.

Shotgun: the table step stops on a report code

Symptom

The run stops at rule finalize_otu_table with a message like this one (wrapped here):

[finalize_otu_table] SRR123_report.txt: Eukaryota (taxid 2759) is a domain in the
Kraken2 database set as references.kraken_db, but the report codes it R2, not D.
Kraken2 releases before 2.17 write reports like this (they do not know the rank name
'domain'), and so do reports made with another database. Run Kraken2 again on
references.kraken_db with the release MetaFlux pins (workflow/envs/kraken2.yaml), for
example with -R kraken2.

Its relatives say that a taxon "is coded D, but it is not a domain", that a kingdom is not coded K (or a K row is not a kingdom), or that one taxon "has two lineages". No table is written.

Cause. The OTU table takes its domain and kingdom slots from the rank codes of the Bracken reports, and MetaFlux checks every D and K code against the taxonomy of the database named in references.kraken_db, its taxo.k2d file. The two disagree when the Kraken2 reports were written by a Kraken2 older than 2.17, which codes NCBI's domains R1 or R2 (a run started without --sdm conda uses whatever Kraken2 is on the PATH), or when they were made with another database than the one the config names now. Reports of two databases can also give one taxon two lineages. A table built from such reports would have an empty d__ slot in every row, or slots filled from another taxonomy, with no other sign that anything is wrong.

Fix. Run Kraken2 again with MetaFlux's pinned environment (--sdm conda), against the database the config names; -R kraken2 forces it when Snakemake sees nothing to redo. If instead the reports are right and the config names the wrong database, point references.kraken_db back at the database they were made with.

Start-up: the run refuses a keep/discard token

Symptom

The run stops before any step starts, with a message like this one (an amplicon run; in a shotgun run the message names shotgun.taxonomy_filter and the Kraken2 database):

[MetaFlux] these amplicon.taxonomy.filter tokens can never match a taxonomy string of this run (16S, method sintax, release 144), so the run stops before any step:
  - amplicon.taxonomy.filter.keep: 'k__Bacteria' puts a domain under k__, which holds kingdoms in this table. Bacteria is a domain: write d__Bacteria.
  - amplicon.taxonomy.filter.keep: 'k__Archaea' puts a domain under k__, which holds kingdoms in this table. Archaea is a domain: write d__Archaea.
A token must equal one whole slot of the taxonomy string, prefix included. Since v2.5.0 the domain is always written d__, and k__ is the kingdom where the reference has one.

Cause. The taxon filter compares each token with whole slots of the taxonomy strings, so a token that no string of the run can contain would remove every ASV or taxon in keep, or nothing in discard, and the run would only find out at its end. MetaFlux therefore checks the tokens when it starts, also with the filter switched off. The usual cause is a config written for MetaFlux v2.4.0 or earlier, which wrote the domain under k__ (k__Bacteria); since v2.5.0 d__ is always the domain and k__ always the kingdom. The message names the other causes too: a prefix the marker, method or SILVA release never writes (k__ on SILVA 138.2 or rpoB, d__ on ITS, s__ on 16S under sintax), a name that cannot stand in that slot of the reference (k__Fungi on 16S), and in a shotgun run Viruses, or a virus realm under k__.

Fix. Write the token the message names (d__Bacteria for k__Bacteria) and run the same command again; nothing has run yet, so nothing is lost. The rules, and what the check cannot see, are under when MetaFlux refuses a token at start-up.

Shotgun: the taxon filter removed every taxon

Symptom

The run stops at rule finalize_otu_table with a message like this one (wrapped here; the numbers depend on the run):

[finalize_otu_table] shotgun.taxonomy_filter removed every taxon of the table
(111 taxa; keep: [p__Firmicutes], discard: []). The d__ values in this table are:
d__Archaea (1 taxon), d__Bacteria (107 taxa), d__Eukaryota (3 taxa). A token must
equal one whole slot of the lineage, prefix included (d__ k__ p__ c__ o__ f__ g__
s__). Check the tokens, or set shotgun.taxonomy_filter.enabled: false.

No otu_table.tsv or otu_table.biom is written.

Cause. shotgun.taxonomy_filter compares each token with whole slots of the lineage, so a token with a name the table does not have matches nothing. A keep list in which no token matches removes every row, and so does a discard list that covers every row. An empty table would look like a finished run, so MetaFlux stops instead, as the amplicon filter does. Tokens that can never match any table (a wrong prefix, a domain under k__) stop the run at start-up already (see the entry above), but names below the kingdom are not checked there, so a misspelt name, or one the database does not use, gets this far: NCBI's taxonomy names the phylum p__Bacillota, not p__Firmicutes.

Fix. Compare the tokens with the d__ values the message lists, or with the taxonomy column of a run made with shotgun.taxonomy_filter.enabled: false, correct them and run the same command again. Kraken2 and Bracken do not run again; the table is built again from the Bracken reports already in 03.abundance/. To keep every taxon, switch the filter off.

Amplicon: ASV counts or taxonomy calls differ between identical re-runs

Three steps in the amplicon path draw on a random-number generator, so two runs on the exact same input can differ slightly:

Step Rule What the random draw is for
DADA2 learnErrors(randomize = TRUE) dada_seqtab Subsamples reads to build the error model
DADA2 assignTaxonomy (method: rdp) assign_taxonomy Bootstrap confidence
VSEARCH --sintax (method: sintax) assign_taxonomy Bootstrap confidence, seeded from a random source by default

Without a fixed seed each of these draws a different sample every run, so a handful of ASVs or taxonomy calls sitting near a decision threshold can flip between two runs. This is expected DADA2/VSEARCH behaviour — not a bug, and not corrupted data.

Before v2.4.0 the order of the reads also differed between runs, and the seed had no effect on it: bowtie2 removed phiX on several threads and wrote the surviving pairs in whatever order they finished, so every run handed DADA2 the same reads in a different sequence (pairing was never broken, and cutadapt and filterAndTrim only drop reads, they never reorder). DADA2 numbers ASVs by decreasing abundance and keeps the input order for ties, so two ASVs with exactly the same total count could swap IDs between runs. Fixed in v2.4.0: bowtie2 now runs with --reorder, so every run hands DADA2 the reads in the same order and gets the same ASV sequences and counts back. If you are reading output from an earlier version, compare runs by sequence rather than by ASV ID. See Reproducibility for what this meant for the optional phylogeny stage, which is sensitive to the order of the alignment rows.

amplicon.seed (42 by default) fixes the generator for all three, and the thread count then no longer changes the result:

  • learnErrors: reproducible at any thread count.
  • --sintax: reproducible at any thread count, from v2.5.0 on. MetaFlux v2.5.0 runs VSEARCH 2.32, which gives every ASV its own random stream. Older VSEARCH releases shared one stream between all threads, so up to v2.4.0 the sintax results were identical only on one thread (resources.threads.assign_taxonomy: 1). A new VSEARCH release can also draw differently, as 2.32 did, so compared with a run made with an older MetaFlux version many confidence values move slightly, and a lineage near the cutoff can gain or lose a trailing rank, even with the same seed. The raw _sintax_raw.tsv lists the ASVs in the order the threads finish them, so sort it before comparing two runs; the tables built from it do not depend on that order. All of this needs a positive amplicon.seed: VSEARCH reads 0 as "pick a random seed".
  • assignTaxonomy (rdp): reproducible at any thread count, with one exception. DADA2's assignTaxonomy breaks exact ties between equally good reference lineages at random, outside the seed, so a few bootstrap values can differ between identical runs; sintax, the default, is not affected. Now and then such a tie flips an ASV's whole lineage between two identical runs, even its kingdom: on the ITS test set (PRJNA862334), ASV_16 had an orchid as best hit in 8 of 11 identical runs and Saccharomyces in the other 3. Its kingdom support reached min_boot: 80 in only one of those three, so keep: [k__Fungi] kept the ASV in 1 run and removed it in the other 10.

Amplicon: DADA2's error model never converges

Symptom

Rule dada_seqtab runs for hours. Its log (logs/dada_seqtab.log in the output directory) keeps adding selfConsist step lines and never reaches the Convergence after N rounds. line that normally follows them:

[dada_seqtab] Learning error rates (R1, nbases=1e+08)...
396568980 total bases in 1468774 reads from 1 samples will be used for learning the error rates.
selfConsist step 1 .
   selfConsist step 2
   selfConsist step 3
   ...
   selfConsist step 172

On the COI mock libraries the marker was validated on, each model converged in 4 to 32 rounds and dada_seqtab took 5 to 23 minutes.

Cause. DADA2's learnErrors fits one error model per read file: for every quality score, how often each base is read as another base. It assumes that every read in the file was produced the same way, and it repeats two steps, denoising the reads with the current error rates and estimating the rates again from the result, until a round gives exactly the same error table as an earlier round. A file whose reads do not share one error profile can keep the estimate moving. Three ways that happens:

  • Reads of both strands in one file. When some reads are reverse complements of the others, the sequencer's errors appear mirrored in part of the file (a true A read as G in the forward reads is a T read as C in the reversed ones), and the poor bases of the reversed reads sit at the start of the read instead of the end, where no truncation removes them.
  • Reads that still carry primer bases. A primer with degenerate positions leaves the reads of one template differing from each other at several positions of high quality, which to DADA2 look either like real variants or like errors at a quality score that should hardly have any.
  • A folder trimmed under orientation: mixed by MetaFlux v2.5.0 or earlier, which has both problems at once: the old second pass reverse-complemented the reversed pairs instead of swapping their mates, and the first pass also kept, through a chance match of three primer bases, pairs that were the other way round with most of a primer still on.

Measured on a genuinely mixed COI library (two libraries of the same insect mock in opposite orientations, 1.6 million pairs as one sample) trimmed the old way: of the R1 reads DADA2 learned from, half were reverse-complemented, with their quality running backwards, and 7 % still began with 17 bases of the primer; the R1 model had not converged after 171 rounds and 10.5 hours when the run was stopped. The same pairs trimmed with v2.6.0 converged in 6 and 5 rounds. pick_trunclen had shown the problem first: with the poor last bases of the reversed reads now at the start of the read, the lower quartile of the quality is below the Q20 threshold at the very first cycle (18 in R1; 16 and 18 in the first two cycles of R2) and at or above it afterwards, and pick_trunclen cuts before the first cycle that fails, so the quality-based cut lands at 0 (Primary cuts at Q1>=20.0: R1=0, R2=0 and WARNING: very short primary cuts in logs/pick_trunclen.log) and the truncation lengths come from raise_trunc alone (270 / 160 here).

MetaFlux sets amplicon.dada2.learn_errors.max_consist to 999, so the job runs on until the model converges, someone stops it, or it reaches round 999; at 3.7 minutes a round, as on this library, that is about 60 hours for the R1 model alone. At the limit DADA2 prints Self-consistency loop terminated before convergence. and continues with the last estimate, as it does after its own default of 10 rounds, and the run finishes, with a model fitted to a mixture and tables made from it.

Fix. Stop the run and check the trimmed reads before the error model. In 03.stripped/{sample}_R1.fastq.gz every read should start with the first bases after the forward primer, and the quality of a read should fall towards its end, not rise; a read that starts with the primer itself, or with the last bases of the other primer, was not trimmed cleanly. With orientation: mixed, stats/orientation/{sample}.tsv says how many pairs each pass kept; a folder trimmed by MetaFlux v2.5.0 or earlier has no such file, and a run with v2.6.0 trims it again, with everything after it (see the mixed-orientation second pass). If the reads were trimmed before MetaFlux saw them, or the two files of a sample are swapped, correct the input: a library in which every pair is swapped is run with its two files swapped and orientation: fixed, a library with pairs in both orientations with mixed.

Amplicon: an outdated marker pack in config/markers/ stops the run

Symptom

Every amplicon run stops before any step starts, whichever marker it uses, with:

[MetaFlux] config/markers/18S.yaml was written for an older MetaFlux: it has no reference_values. A pack in config/markers/ replaces the shipped one completely, so copy workflow/markers/18S.yaml again and re-apply your change.

Cause. A pack in config/markers/ replaces the shipped pack of the same name completely, so an old copy would bring back everything the shipped pack has changed since: an 18S pack from MetaFlux v2.4.0 would classify against PR2's own letter order, which hides the domain of many ASVs. Since v2.5.0 every pack declares reference_values, the rules its reference file is checked against before classification, so a pack without them was written for an older MetaFlux. Every pack in config/markers/ is checked on every amplicon run, which is why an outdated 18S pack stops a 16S run too. A pack for a marker of your own without reference_values gets a message of its own, which says what to add.

Fix. Copy the shipped pack (workflow/markers/<marker>.yaml) to config/markers/ again and re-apply your change, or delete the copy if the shipped pack now does what you need. For a marker of your own, add reference_values with rules for its top ranks, or reference_values: {} to have only the rank shape checked (see the reference check). An 18S pack that does declare reference_values but keeps PR2's original SINTAX letters (k, d, p, …) stops under method: sintax with a message about VSEARCH's fixed letter order; the fix is the same.

Amplicon: the reference check stops the run

Symptom

The run stops at rule check_taxonomy_reference, before any classification, with a message like this one (shortened):

[check_taxonomy_reference] /shared/dbs/silva_nr99_v144_toGenus_7rank_other-verbatim_trainset.fa.gz does not hold the ranks declared by the 16S marker pack, release 138.2, method rdp (Domain, Phylum, Class, Order, Family, Genus, Species):
  - a lineage deeper than the 6 ranks the rank model expects in this file (the Species rank comes from addSpecies): 473,550 header(s)
      e.g. >Bacteria;Pseudomonadati;Pseudomonadota;Gammaproteobacteria;Pseudomonadales;Pseudomonadaceae;Pseudomonas;
  - the deepest lineage has 7 ranks, but the rank model expects 6 in this file (the Species rank comes from addSpecies)
  The classifier would put names on the wrong ranks, so the run stops before any classification. Either the run config points at a wrong file (references:, and for 16S the SILVA release chosen with references.silva.release), or the reference changed upstream and its marker pack needs a new release block (see the reference_values notes in workflow/markers/16S.yaml).

Cause. A classifier never checks what a rank means: if a reference has one rank more or less than the marker pack expects, or two ranks swapped, every name lands in the wrong column and nothing errors. So before any classification MetaFlux checks the exact file the classifier is about to read against the rank set and the names its pack declares (see the reference check). The message names the file, what does not fit, and example headers. Almost always a path under references: points at the wrong file: in the example, a SILVA 144 training set (seven ranks, with the kingdom) pinned as silva.train for a run on SILVA 138.2 (six ranks). Less often the reference itself has changed upstream.

Fix. Point the references: entry at the file that belongs to the run's marker and SILVA release, or remove it, so that MetaFlux uses, and if needed builds, its own (see reference files MetaFlux builds itself). For 16S, keep references.silva.release and the pinned SILVA files together. If the reference itself has changed (a new release with another rank), the marker pack needs a new release block. logs/refdb/check_taxonomy_reference.log in the output directory lists every problem the check found.

Amplicon: a reference download stops with "md5 mismatch"

Symptom

A fetch_* rule fails, and its log (logs/refdb/fetch_<name>.log in the output directory) ends with one of these lines (the second comes from the COI downloads, whose MIDORI2 FASTA and bacterial reference files are checked against SHA-256 sums):

md5 mismatch for <file>: expected …, got …
sha256 mismatch for <file>: expected …, got …

Cause. The downloaded file is not the one the marker pack expects: the download was cut off, a proxy returned an error page, or the file behind the address has changed. MetaFlux deletes the file, so it is never used.

Fix. Run the same command again; a broken download usually works the second time. If the same checksum comes back every time, the file on the server has changed. Report it as an issue on GitHub rather than editing the md5 or SHA-256 in the marker pack.

Config (YAML) errors

Snakemake reports any config problem with one generic message ("Config file is not valid JSON or YAML…"). That almost always means a YAML indentation or list-syntax issue, or a stray tab — not a problem with the parameter values themselves. Validate the file directly to get the exact line:

python -c "import yaml; yaml.safe_load(open('config/config.yaml'))"

A "expected block end, but found block sequence start" message points to a misplaced or over-indented - list item — commonly in host_genomes.

The full key-by-key reference is in Configuration.