grafana/k6 · error
99
99
Error message
Thresholds have been crossed
What it means
After a `k6 cloud run` finishes, k6 inspects the final TestProgress: if testProgress.ThresholdsFailed() is true, this error is returned with exit code 99 (ThresholdsHaveFailed). It is not a malfunction — it is k6's designed contract of converting a failed threshold into a non-zero exit so CI gates can act on it.
Source
Thrown at internal/cmd/cloud.go:389
globalCancel()
break
}
}
// Stop progress rendering before printing final status to avoid ghost bars.
progressCancel()
progressBarWG.Wait()
if testProgress == nil {
//nolint:staticcheck
return errext.WithExitCodeIfNone(errors.New("Test progress error"), exitcodes.CloudFailedToGetProgress)
}
c.printTestStatus(testProgress.FormatStatus())
if testProgress.ThresholdsFailed() {
//nolint:staticcheck
return errext.WithExitCodeIfNone(errors.New("Thresholds have been crossed"), exitcodes.ThresholdsHaveFailed)
}
if testProgress.TestFailed() {
//nolint:staticcheck
return errext.WithExitCodeIfNone(errors.New("The test has failed"), exitcodes.CloudTestRunFailed)
}
return nil
}
func (c *cmdCloud) printTestStatus(status string) {
if !c.gs.Flags.Quiet {
valueColor := getColor(c.gs.Flags.NoColor || !c.gs.Stdout.IsTTY, color.FgCyan)
printToStdout(c.gs, fmt.Sprintf(
" test status: %s\n", valueColor.Sprint(status),
))
} else {
c.gs.Logger.WithField("run_status", status).Debug("Test finished")
}View on GitHub (pinned to 93accf6570)
Solutions
- Open the test run in Grafana Cloud and check the Thresholds tab to see which threshold crossed and by how much
- Tune thresholds to values that reflect an acceptable SLO for the current environment
- Fix the performance issue in the target system or reduce load if the thresholds are correct
- In CI, treat exit code 99 as the deliberate quality-gate signal rather than retrying blindly
Example fix
// before: threshold set below real p(95)
export const options = {
thresholds: { http_req_duration: ['p(95)<200'] },
};
// after: align the threshold with the measured SLO
export const options = {
thresholds: { http_req_duration: ['p(95)<500'] },
}; Defensive patterns
Strategy: validation
Validate before calling
// script-side: keep thresholds in a shared module so local and cloud runs
// assert the same SLOs before anything is uploaded
import { thresholds } from './slo.js';
export const options = { thresholds }; Try / catch
err := runK6("cloud", "run", "script.js")
if exitCode(err) == 99 { // exitcodes.ThresholdsHaveFailed
// deliberate quality gate: collect the failing thresholds from the run URL
// and fail the pipeline step with that context
} Prevention
- Baseline the target system first, then set thresholds with headroom above measured p(95)/p(99)
- Treat exit code 99 as the designed CI gate signal, not a flaky failure to retry
- Read the printed 'test status:' line and the run's Thresholds tab to see exactly which threshold crossed
When it happens
Trigger: The cloud test run completes and the service reports at least one crossed threshold (e.g. http_req_duration p(95) above the configured limit). The final status line ('test status: ...') is printed before the error, naming the failing condition.
Common situations: Target system slower than the thresholds assume; thresholds copied from another environment; scaled-up load in cloud runs crossing limits that local runs passed; intentionally strict thresholds used as CI quality gates.
Related errors
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/3293233b670ad5dd.
Report an issue: GitHub.