VictoriaMetrics/VictoriaMetrics · error

cannot unmarshal Positive: %w

Error message

cannot unmarshal Positive: %w

What it means

Wrap of the error returned by buckets.decodeBuckets when unmarshaling the `positive` Buckets submessage (field 8) of an ExponentialHistogramDataPoint. The embedded message bytes were obtained, but their internal fields (offset / bucket_counts) could not be parsed. The wrapped error names the exact inner field that failed.

Source

Thrown at lib/protoparser/opentelemetry/pb/pb.go:1377

			}
			ehctx.hasSum = true
		case 6:
			ehctx.scale, ok = fc.Sint32()
			if !ok {
				return fmt.Errorf("cannot read Scale")
			}
		case 7:
			ehctx.zeroCount, ok = fc.Fixed64()
			if !ok {
				return fmt.Errorf("cannot read ZeroCount")
			}
		case 8:
			data, ok := fc.MessageData()
			if !ok {
				return fmt.Errorf("cannot read Positive buckets")
			}
			if err := ehctx.positive.decodeBuckets(data); err != nil {
				return fmt.Errorf("cannot unmarshal Positive: %w", err)
			}
		case 9:
			data, ok := fc.MessageData()
			if !ok {
				return fmt.Errorf("cannot read Negative buckets")
			}
			if err := ehctx.negative.decodeBuckets(data); err != nil {
				return fmt.Errorf("cannot unmarshal Negative: %w", err)
			}
		case 10:
			ehctx.flags, ok = fc.Uint32()
			if !ok {
				return fmt.Errorf("cannot read Flags")
			}
		case 12:
			ehctx.min, ok = fc.Double()
			if !ok {
				return fmt.Errorf("cannot read Min")

View on GitHub (pinned to 5079fb58f1)

Solutions

  1. Inspect the wrapped inner error to see which bucket field failed, then fix the producer serialization for that field.
  2. Re-send the batch; if reproducible, decode the payload with google.golang.org/protobuf to confirm the message is invalid per the proto spec.
  3. Align opentelemetry-proto versions between the SDK/collector emitting metrics and the ingestion endpoint.
  4. If you are writing the exporter yourself, use the generated proto marshalers instead of hand-packing bucket fields.

Example fix

// before: hand-packed buckets with varint wire type for field 1
mm.AppendInt32(1, buckets.Offset)
// after: offset is sint32 in proto; use the generated marshaler
bucketMsg := &metricspb.ExponentialHistogramDataPoint_Buckets{Offset: buckets.Offset, BucketCounts: buckets.Counts}
mm.AppendMessage(8) // then marshal bucketMsg into it
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate with the generated types, which check nested Buckets fields too
var req metricspb.ExportMetricsServiceRequest
if err := proto.Unmarshal(payload, &req); err != nil {
    return fmt.Errorf("invalid OTLP payload: %w", err)
}

Try / catch

if err := decode(data); err != nil {
    var inner error
    if errors.Unwrap(err) != nil {
        inner = errors.Unwrap(err) // reveals which bucket field failed
    }
    log.Error("failed to decode positive buckets", "wrapped", inner)
    metrics.MalformedPayloads.Inc()
    return nil // skip instead of failing the whole request
}

Prevention

When it happens

Trigger: Parsing OTLP metrics where the positive Buckets submessage contains field 1 (offset) or field 2 (bucket_counts) with an unexpected wire type, or the submessage bytes are internally truncated so NextField fails.

Common situations: Version drift between producers and consumers of opentelemetry-proto (a field repurposed with a different type), corrupted telemetry queues, or fuzz/hand-written exporters emitting malformed bucket data.

Related errors


AI-assisted analysis of VictoriaMetrics/VictoriaMetrics@5079fb58f1 (2026-09-03). Data as JSON: /api/errors/b45061242c152dd5. Report an issue: GitHub.