grafana/k6 · error · ErrInvalidMetricType

invalid metric type

Error message

invalid metric type

What it means

ErrInvalidMetricType (metrics/metric_type.go:20) is returned by MetricType.UnmarshalText (line 70) when the string being deserialized is not one of 'counter', 'gauge', 'trend', 'rate' — and symmetrically by MarshalText (line 54) for an out-of-range MetricType integer. It surfaces through UnmarshalJSON/MarshalJSON whenever a metric's serialized type round-trips: registry JSON, k6 archive inspection, or summary/output serialization.

Source

Thrown at metrics/metric_type.go:20

import (
	"errors"
	"slices"
)

// A MetricType specifies the type of a metric.
type MetricType int

// Possible values for MetricType.
const (
	Counter = MetricType(iota) // A counter that sums its data points
	Gauge                      // A gauge that displays the latest value
	Trend                      // A trend, min/max/avg/med are interesting
	Rate                       // A rate, displays % of values that aren't 0
)

// ErrInvalidMetricType indicates the serialized metric type is invalid.
var ErrInvalidMetricType = errors.New("invalid metric type")

const (
	counterString = "counter"
	gaugeString   = "gauge"
	trendString   = "trend"
	rateString    = "rate"

	defaultString = "default"
	timeString    = "time"
	dataString    = "data"
)

// MarshalJSON serializes a MetricType as a human readable string.
func (t MetricType) MarshalJSON() ([]byte, error) {
	txt, err := t.MarshalText()
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to 93accf6570)

Solutions

  1. Use exactly one of: counter, gauge, trend, rate (lowercase)
  2. If interoperating with an older format, map legacy names to the four supported types before unmarshaling
  3. When the value comes from user input, validate it against the accepted set before decoding

Example fix

// before
var mt metrics.MetricType
err := json.Unmarshal([]byte(`"Histogram"`), &mt) // ErrInvalidMetricType

// after
var mt metrics.MetricType
err := json.Unmarshal([]byte(`"trend"`), &mt)
Defensive patterns

Strategy: type-guard

Validate before calling

// Go: whitelist check before unmarshaling external strings
var validMetricTypes = map[string]bool{"counter": true, "gauge": true, "trend": true, "rate": true}
if !validMetricTypes[input] {
    return fmt.Errorf("unsupported metric type %q; want counter|gauge|trend|rate", input)
}

Type guard

// Go
func isValidMetricType(s string) bool {
    switch s {
    case "counter", "gauge", "trend", "rate":
        return true
    }
    return false
}

Try / catch

// Go
if err := json.Unmarshal(data, &mt); err != nil {
    if errors.Is(err, metrics.ErrInvalidMetricType) {
        // map legacy names (e.g. "histogram" -> "trend") and retry, or reject input
    }
}

Prevention

When it happens

Trigger: json.Unmarshal into a struct with a metrics.MetricType field given '"histogram"', '"COUNTER"' (case-sensitive), or '""'; marshaling a MetricType constructed from an invalid int like MetricType(7).

Common situations: Consuming metric payloads from old k6 versions or third-party tools that emit legacy type names (e.g. pre-0.4 'histogram' types); hand-built JSON metric definitions with typos or wrong casing; corrupted exported archives.

Related errors


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