thanos-io/thanos · error

invalid temporality for metric

Error message

invalid temporality for metric %q

What it means

FromMetrics iterates all OTLP metrics and checks isValidAggregationTemporality per metric. Only cumulative (and delta for certain types) temporality is supported for Prometheus remote-write translation; a metric with an unsupported temporality (e.g. unspecified) adds this error and skips the metric.

Solutions

  1. Configure the exporter/SDK to emit cumulative temporality (default) or the temporality the receiver accepts
  2. Fix producers that construct metrics without setting AggregationTemporality explicitly
  3. Use the collector deltatocumulative or aggregate processors to convert delta to cumulative before forwarding
  4. Check which metric names are listed in the error set and align their instrument temporality settings

Example fix

# before: exporter configured for unsupported temporality
otlp: 
  temporality_preference: delta
# after
otlp:
  temporality_preference: cumulative
Defensive patterns

Strategy: validation

Validate before calling

for _, m := range ms {
    if !isValidAggregationTemporality(m) {
        log.Warnf("metric %q has unsupported temporality %v", m.Name(), m.AggregationTemporality())
    }
}

Type guard

func supportedTemporality(m pmetric.Metric) bool {
    switch m.Type() {
    case pmetric.MetricTypeHistogram:
        return m.Histogram().AggregationTemporality() == pmetric.AggregationTemporalityCumulative
    case pmetric.MetricTypeExponentialHistogram:
        return m.ExponentialHistogram().AggregationTemporality() == pmetric.AggregationTemporalityCumulative
    case pmetric.MetricTypeSum:
        return m.Sum().AggregationTemporality() == pmetric.AggregationTemporalityCumulative
    default:
        return true
    }
}

Try / catch

_, err := translator.FromMetrics(ctx, md, settings)
if err != nil {
    var annots annotations.Annotations
    if errors.As(err, &annots) {
        for _, a := range annots {
            if strings.Contains(a.Error(), "invalid temporality") {
                log.Warnf("skipped metric: %v", a)
                continue
            }
        }
    }
}

Prevention

When it happens

Trigger: Sending OTLP metrics whose AggregationTemporality is TemporalityUnspecified or a temporality unsupported for that metric type (e.g. delta gauge-like sums depending on feature gates) through the OTLP->PRW converter.

Common situations: Producers/SConstructors that forget to set temporality on histogram/sum instruments; gateways stripping temporality; mixing cumulative and delta streams; OTLP exporters configured for delta when the receiver only accepts cumulative.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07). Data as JSON: /api/errors/c0d77609b2174c2d. Report an issue: GitHub.

Appendix: source

Thrown at pkg/receive/otlptranslator/metrics_to_prw.go:86

		scopeMetricsSlice := resourceMetrics.ScopeMetrics()
		// keep track of the most recent timestamp in the ResourceMetrics for
		// use with the "target" info metric
		var mostRecentTimestamp pcommon.Timestamp
		for j := 0; j < scopeMetricsSlice.Len(); j++ {
			metricSlice := scopeMetricsSlice.At(j).Metrics()

			// TODO: decide if instrumentation library information should be exported as labels
			for k := 0; k < metricSlice.Len(); k++ {
				if err := c.everyN.checkContext(ctx); err != nil {
					errs.Add(err)
					return
				}

				metric := metricSlice.At(k)
				mostRecentTimestamp = max(mostRecentTimestamp, mostRecentTimestampInMetric(metric))

				if !isValidAggregationTemporality(metric) {
					errs.Add(fmt.Errorf("invalid temporality for metric %q", metric.Name()))
					continue
				}

				promName := BuildCompliantName(metric, settings.Namespace, settings.AddMetricSuffixes, settings.AllowUTF8)
				c.metadata = append(c.metadata, prompb.MetricMetadata{
					Type:             otelMetricTypeToPromMetricType(metric),
					MetricFamilyName: promName,
					Help:             metric.Description(),
					Unit:             metric.Unit(),
				})

				// handle individual metrics based on type
				//exhaustive:enforce
				switch metric.Type() {
				case pmetric.MetricTypeGauge:
					dataPoints := metric.Gauge().DataPoints()
					if dataPoints.Len() == 0 {
						errs.Add(fmt.Errorf("empty data points. %s is dropped", metric.Name()))

View on GitHub (pinned to 35b8b99117)