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.
✗ http_req_duration..............: avg=812ms p(95)=1.94s
{ expected: p(95)<500 }
ERRO[0031] some thresholds have been crossed
exit status 99Common 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
- Find the threshold line marked with a cross in the summary.
- Confirm whether the app regressed or the target was starved of resources.
- Fix the code path, or right-size the target service before re-running.
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.
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.