{"record":{"id":"714425729d8824b3","repo":"grafana/k6","slug":"99-714425","errorCode":"99","errorMessage":"thresholds on metrics '%s' have been crossed","messagePattern":"thresholds on metrics '(.+?)' have been crossed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cmd/run.go","lineNumber":376,"sourceCode":"\t}()\n\n\tif thresholdsEnabled {\n\t\tfinalizeThresholds := metricsEngine.StartThresholdCalculations(\n\t\t\tmetricsIngester, runAbort, executionState.GetCurrentTestRunDuration,\n\t\t)\n\t\thandleFinalThresholdCalculation := func() {\n\t\t\t// This gets called after the Samples channel has been closed and\n\t\t\t// the OutputManager has flushed all of the cached samples to\n\t\t\t// outputs (including MetricsEngine's ingester). So we are sure\n\t\t\t// there won't be any more metrics being sent.\n\t\t\tlogger.Debug(\"Finalizing thresholds...\")\n\t\t\tbreachedThresholds := finalizeThresholds()\n\t\t\tif len(breachedThresholds) == 0 {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttErr := errext.WithAbortReasonIfNone(\n\t\t\t\terrext.WithExitCodeIfNone(\n\t\t\t\t\tfmt.Errorf(\"thresholds on metrics '%s' have been crossed\", strings.Join(breachedThresholds, \", \")),\n\t\t\t\t\texitcodes.ThresholdsHaveFailed,\n\t\t\t\t), errext.AbortedByThresholdsAfterTestEnd)\n\n\t\t\tif err == nil {\n\t\t\t\terr = tErr\n\t\t\t} else {\n\t\t\t\tlogger.WithError(tErr).Debug(\"Crossed thresholds, but test already exited with another error\")\n\t\t\t}\n\t\t}\n\t\tif finalizeThresholds != nil {\n\t\t\tdefer handleFinalThresholdCalculation()\n\t\t}\n\t}\n\n\tdefer func() {\n\t\tlogger.Debug(\"Waiting for metrics processing to finish...\")\n\t\tclose(samples)\n\t\twaitOutputsFlushed()","sourceCodeStart":358,"sourceCodeEnd":394,"githubUrl":"https://github.com/grafana/k6/blob/3fcf5388d78cb382f0c0d42ec556a06bfd1b8dee/internal/cmd/run.go#L358-L394","documentation":"After the test ends and all samples are flushed to outputs, k6 evaluates thresholds one final time. If any were crossed, the run is failed with the list of breached metrics, exit code 99 (ThresholdsHaveFailed), and abort reason AbortedByThresholdsAfterTestEnd. This is k6's SLO gate: execution completed normally, but the acceptance criteria in options.thresholds were not met.","triggerScenarios":"Declaring thresholds like `export const options = { thresholds: { http_req_duration: ['p(95)<300'] } }` (or http_req_failed, checks, custom metrics) whose measured values exceed the limit at end of test; also applies to k6 cloud runs.","commonSituations":"CI pipelines using k6 as a pass/fail performance gate; regression detection after a deploy; thresholds copied from another environment with very different latency baselines.","solutions":["Check which metrics crossed — the message names them and the end-of-test summary marks those threshold rows ✗","Fix the underlying performance problem in the system under test (the usual intent of the gate)","If a threshold is wrong or unrealistic, adjust it in export const options = { thresholds: {...} }","In CI, map exit code 99 to a distinct 'performance gate failed' status and archive the summary report"],"exampleFix":"// before\nexport const options = {\n  thresholds: { http_req_duration: ['p(95)<100'] },\n};\n\n// after (if 100ms was an unrealistic target for this environment)\nexport const options = {\n  thresholds: { http_req_duration: ['p(95)<300'] },\n};","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"# treat 99 as the performance-gate result, not a generic error\nk6 run script.js\nec=$?\ncase $ec in\n  0) echo \"passed\";;\n  99) echo \"thresholds crossed — performance gate failed\"; exit 99;;\n  *) echo \"k6 failed with exit code $ec\"; exit $ec;;\nesac","preventionTips":["Baseline new thresholds with a smoke run before enforcing them in CI gates","Set thresholds from realistic measured values (p95 plus headroom), not aspirational numbers","Keep threshold definitions in one reviewed place in the script so failures are traceable","Know the exit codes: 99 thresholds, 105 aborted, 110 script-marked-failed, 104 invalid config"],"tags":["k6","thresholds","metrics","exit-code-99","performance"],"backgroundTag":"load-test-thresholds-failed","analyzedSha":"3fcf5388d78cb382f0c0d42ec556a06bfd1b8dee","analyzedAt":"2026-08-18T03:05:52.393Z","contentChangedAt":"2026-08-18T03:05:52.393Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}