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

  1. Open the test run in Grafana Cloud and check the Thresholds tab to see which threshold crossed and by how much
  2. Tune thresholds to values that reflect an acceptable SLO for the current environment
  3. Fix the performance issue in the target system or reduce load if the thresholds are correct
  4. 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

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.