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
- 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, ...)
- If the method is unsupported, match it to the type: trend -> avg/min/max/med/p(95); counter -> count/rate; gauge -> value; rate -> rate
- 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
- Match aggregation method to metric type: trend->avg/min/max/med/p(NN), counter->count/rate, gauge->value, rate->rate
- Threshold targets must exist in the registry: create custom metrics with newCounter/newTrend/... before referencing them
- Note that thresholds on sub-metrics like 'http_req_duration{status:200}' inherit the parent's type (trend)
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
- urlTemplate must contain {key} placeholder
- urlTemplate must be an absolute URL with a scheme (e.g., htt
- timeout must be greater than 0
- requestsPerMinuteLimit must be greater than 0
- requestsBurst must be greater than 0
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/6931b5399af4599f.
Report an issue: GitHub.