What does k6's --no-thresholds flag change about a run's exit code and its validation?
answer
- stops judging, keeps measuring
- the verdict codes cannot fire
- also skips the pre-run rule check
- runtime option, not a script option
basics
~20 sk6's --no-thresholds flag switches off threshold evaluation and threshold validation for the whole run, so a breach can no longer produce exit 99 and a malformed rule no longer produces exit 104. The run exits 0.
solid answer
~40 s`--no-thresholds` makes a `k6 run` stop judging. k6 still generates load, collects every metric and prints the summary, but the evaluation passes never execute, so no breach can produce **99**. Less obviously, it also skips the pre-run parse and validation of every threshold expression — so a malformed rule, or one naming a metric that does not exist, no longer aborts with **104**; it degrades to a log warning. The flag is a k6 runtime option, settable only via the CLI or `K6_NO_THRESHOLDS`; it is not a field of the script's exported `options` object. Its honest use is an instance that streams samples elsewhere and is not the judge of the whole test.
code
bash · 10 lines# A script whose threshold names a metric that does not exist
k6 run bad-rule.js
echo "strict: $?" # 104 - rejected before any load
k6 run --no-thresholds bad-rule.js
echo "relaxed: $?" # 0 - rule never parsed, test runs
# The environment variable is the same switch
K6_NO_THRESHOLDS=true k6 run bad-rule.js
echo "env: $?" # 0go deeper
Know that the flag stops k6 judging: load is still generated and metrics still collected, but no breach can make the process exit non-zero.
Explain both halves — evaluation is skipped and pre-run validation is skipped — and that the flag is CLI or environment only, never a field of the exported options object.
Name the failure mode: a misspelled metric in a rule degrades from a hard 104 at load to a log warning, so the rule silently never applies until someone runs without the flag.
The judgement call is whether a k6 invocation should be a judge or only a generator, because --no-thresholds is the switch that decides it and, once thrown, that run's exit code stops meaning anything.
## What the flag switches off `--no-thresholds` is a `k6 run` flag that turns off **threshold execution** for the whole run. With it set, k6 still generates load, still collects every metric, and still prints the end-of-test summary — it simply stops judging. A run that would have exited **99** exits **0** instead, because there is no evaluation to produce a breach. Internally the flag flips one boolean. `internal/cmd/run.go` computes `thresholdsEnabled := !RuntimeOptions.NoThresholds.Bool`, and the ticking evaluation goroutine and the final evaluation pass both sit behind that flag. Nothing evaluates, so nothing can breach. ## It also switches off validation — the part people miss The flag does more than skip evaluation. k6 normally parses and validates every threshold expression *before the run starts*, and a rule it cannot accept is a configuration error, not a test failure: `internal/cmd/test_load.go` wraps both `Parse()` and `Validate()` failures in `exitcodes.InvalidConfig`, which is **104**. The whole block is guarded by the same `if !RuntimeOptions.NoThresholds.Bool`. So `--no-thresholds` suppresses the 104 as well. k6's own test table records exactly this pair of outcomes for three separate defects: | script defect | plain `k6 run` | with `--no-thresholds` | |---|---|---| | malformed threshold expression | exit 104, no load generated | exit 0, test runs | | rule on a metric that does not exist | exit 104, no load generated | exit 0, test runs | | aggregation the metric's type cannot supply | exit 104, no load generated | exit 0, test runs | Invalid metric names are downgraded to a warning rather than an error: the metrics engine is initialised in log-only mode and prints `Invalid metric '<name>' in threshold definitions` instead of returning. That is the real hazard of the flag — a typo in a rule name is silent, and stays silent until someone runs without the flag. ## Where you can set it, and where you cannot `--no-thresholds` is a **k6 runtime option**, not a member of the script's exported `options` object. The field is `NoThresholds` on `lib.RuntimeOptions`, not on `lib.Options`, which means: - the CLI flag `--no-thresholds` works; - the environment variable `K6_NO_THRESHOLDS=true` works; - putting `noThresholds: true` in the exported `options` object does **not** work — there is no such script option, and k6 will treat it as an unknown field; - a `--config` file cannot set it either, because that file supplies script options. This is the opposite of most k6 settings, and it is deliberate: whether this instance is the judge is a property of *how you invoked k6*, not of the test script, so two invocations of the same script can disagree about it. ## Why a run would want it The honest use is the one k6's own large-test guidance names: when a run streams its samples to an external sink with `--out`, the verdict is meant to be formed by whatever consumes those samples, not by the k6 process. Evaluating locally as well costs the process the memory of keeping every threshold's sink warm for the whole run, and produces a second, competing verdict nobody reads. Turning judging off makes that invocation a pure generator, and its exit code stops carrying a verdict at all. The other honest use is quick local iteration: you want the numbers on screen, but not a red exit code, while you are still changing the script. ## Consequences to be explicit about 1. **The exit code no longer answers "did the test pass?"** With the flag set, a run that violated every rule you wrote exits 0. Any judgement has to come from somewhere else. 2. **Typos become invisible.** The 104 that would have caught a misspelled metric name at load time is exactly what the flag suppresses. 3. **It is per-invocation.** Setting it in one place does not set it for another instance of the same script, so two runs of one file can produce different verdicts. 4. **Nothing else is disabled.** Metric collection, the summary, and configured outputs are all unaffected; only the pass/fail judgement is gone. ## In k6 v2 The flag and the `K6_NO_THRESHOLDS` variable are both current in k6 v2.x, and the documented options reference lists the code/config-file column for this option as **N/A**, matching the source. Note it is `--no-thresholds`, plural, and unrelated to `--summary-mode=disabled`, which suppresses the printed summary while leaving the judging intact.
- Can --no-thresholds be set from inside a k6 script's exported options object?No. `NoThresholds` lives on `lib.RuntimeOptions`, not on `lib.Options`, so there is no `noThresholds` script option; k6 would treat it as an unknown field. Only the `--no-thresholds` flag and the `K6_NO_THRESHOLDS` environment variable set it, and the documented options reference marks the code/config-file column N/A to match.
- What is the risk of leaving --no-thresholds on in a k6 run you intend to trust?Two risks. The run exits 0 even if every rule you wrote was violated, so the exit code stops meaning anything. And because pre-run validation is skipped too, a misspelled metric name in a rule is downgraded from a hard 104 to a log warning — the rule silently never applies, and stays silent until someone runs without the flag.
saying these in an interview costs you the question
- Thinks --no-thresholds still evaluates rules but hides them
- Believes it only suppresses the printed summary
- Sets noThresholds inside the script's options object
- Assumes a malformed rule still fails with 104 under the flag
- Confuses it with --summary-mode=disabled