Skip to content
Latchkey

k6 "thresholds have been crossed" (exit code 99) in CI

k6 exits with code 99 when one or more thresholds you defined were crossed during the run. This is a pass/fail gate working as designed: the load test ran, but a metric (p95 latency, error rate) missed its target.

What this error means

k6 prints a summary with a red cross next to a threshold line and the step fails with "some thresholds have been crossed" and exit code 99, distinct from an execution error.

k6
     ✗ http_req_duration..............: avg=812ms p(95)=1.94s
       { expected: p(95)<500 }
ERRO[0031] some thresholds have been crossed
exit status 99

Common causes

A real metric missed its target

The service under test was slower or errored more than your thresholds allow, so k6 fails the run to block a regression from merging.

The threshold is stricter than the environment can meet

A CI runner or ephemeral target is slower than production, so a p(95)<500 gate calibrated for prod fails on undersized infrastructure.

How to fix it

Read which threshold failed and fix the regression

  1. Find the threshold line marked with a cross in the summary.
  2. Confirm whether the app regressed or the target was starved of resources.
  3. Fix the code path, or right-size the target service before re-running.
script.js
export const options = {
  thresholds: {
    http_req_duration: ['p(95)<500'],
    http_req_failed: ['rate<0.01'],
  },
};

Calibrate thresholds to the CI target

Keep the gate honest by setting thresholds against the environment CI actually tests, not against production hardware.

script.js
thresholds: {
  http_req_duration: ['p(95)<1500'], // CI target is smaller than prod
},

How to prevent it

  • Define thresholds so exit code 99 is the intended release gate.
  • Match threshold targets to the size of the CI environment under test.
  • Track the summary over time so a real regression is obvious.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →