grafana/k6 · error · ErrInvalidThreshold

invalid threshold

Error message

invalid threshold

What it means

ErrInvalidThreshold (metrics/thresholds.go:236) is the sentinel for Thresholds.Validate failures. It fires in two cases: the metric named in the threshold config does not exist in the registry (line 256: 'no metric name %q found'), or a threshold expression uses an aggregation method unsupported by the metric's type (line 276-285, checked via MetricType.supportsAggregationMethod). Both are wrapped with errext.WithExitCodeIfNone(..., exitcodes.InvalidConfig), so k6 exits with the InvalidConfig code.

Source

Thrown at metrics/thresholds.go:236

}

// Parse parses the Thresholds and fills each Threshold.parsed field with the result.
// It effectively asserts they are syntaxically correct.
func (ts *Thresholds) Parse() error {
	for _, t := range ts.Thresholds {
		parsed, err := parseThresholdExpression(t.Source)
		if err != nil {
			return err
		}

		t.parsed = parsed
	}

	return nil
}

// ErrInvalidThreshold indicates a threshold is not valid
var ErrInvalidThreshold = errors.New("invalid threshold")

// Validate ensures a threshold definition is consistent with the metric it applies to.
// Given a metric registry and a metric name to apply the expressions too, Validate will
// assert that each threshold expression uses an aggregation method that's supported by the
// provided metric. It returns an error otherwise.
// Note that this function expects the passed in thresholds to have been parsed already, and
// have their Parsed (ThresholdExpression) field already filled.
func (ts *Thresholds) Validate(metricName string, r *Registry) error {
	parsedMetricName, _, err := ParseMetricName(metricName)
	if err != nil {
		parseErr := fmt.Errorf("unable to validate threshold expressions; reason: %w", err)
		return errext.WithExitCodeIfNone(parseErr, exitcodes.InvalidConfig)
	}

	// Obtain the metric the thresholds apply to from the registry.
	// if the metric doesn't exist, then we return an error indicating
	// the InvalidConfig exitcode should be used.
	metric := r.Get(parsedMetricName)

View on GitHub (pinned to 93accf6570)

Solutions

  1. If the metric name is misspelled or missing, create it with counter/gauge/trend/rate from 'k6/metrics' or correct the name to a built-in metric (http_req_duration, checks, data_received, ...)
  2. If the method is unsupported, match it to the type: trend -> avg/min/max/med/p(95); counter -> count/rate; gauge -> value; rate -> rate
  3. Check errors.Is(err, metrics.ErrInvalidThreshold) and the exit code InvalidConfig when handling programmatically

Example fix

// before
export const options = {
  thresholds: { http_req_duration: ['rate<100'] },
};

// after
export const options = {
  thresholds: { http_req_duration: ['p(95)<800'] },
};
Defensive patterns

Strategy: validation

Validate before calling

// JS: sanity-check threshold keys/methods before a run
const METHODS = {
  counter: ['count', 'rate'], gauge: ['value'], rate: ['rate'],
  trend: ['avg', 'min', 'max', 'med', 'p(90)', 'p(95)', 'p(99)'],
};
for (const [metric, ths] of Object.entries(options.thresholds || {})) {
  const type = metricType(metric); // from your own registry of custom + built-in metrics
  for (const th of ths) {
    const m = th.match(/^(count|rate|value|avg|min|max|med|p\(\d+\))/);
    if (type && (!m || !METHODS[type].includes(m[1]))) {
      throw new Error(`threshold ${th} not valid for ${metric} (${type})`);
    }
  }
}

Try / catch

// Go
if err := th.Validate(name, reg); err != nil {
    if errors.Is(err, metrics.ErrInvalidThreshold) {
        // err already carries exitcodes.InvalidConfig; report and fix config, do not retry
    }
}

Prevention

When it happens

Trigger: thresholds: { 'nonexistent_metric': ['value<10'] }, or a type/method mismatch such as 'http_req_duration': ['rate<100'] (rate is only for counters/rates) or 'checks': ['p(99)<0.9'] (percentiles only for trends). Supported methods: counter -> count,rate; gauge -> value; rate -> rate; trend -> avg,min,max,med,p(NN).

Common situations: Copy-pasting thresholds between metric types; thresholds on custom metrics before they are created (a custom metric referenced only in thresholds is not in the registry); renaming a metric but not its threshold; using value on a counter.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/6931b5399af4599f. Report an issue: GitHub.