jaegertracing/jaeger · error

cannot unmarshal OTLP : %w

Error message

cannot unmarshal OTLP : %w

What it means

otlp2traces decodes raw OTLP JSON bytes into OpenTelemetry ptrace.Traces before converting them to Jaeger model.Trace objects. When ptrace.JSONUnmarshaler.UnmarshalTraces cannot parse the payload (malformed JSON or an OTLP shape it does not accept), the error is wrapped as "cannot unmarshal OTLP : %w" so the underlying decoder reason is preserved. Called via transformOTLP, this is the entry point for OTLP-format input to the query extension.

Source

Thrown at cmd/jaeger/internal/extension/jaegerquery/internal/otlp_translator.go:19

// Copyright (c) 2024 The Jaeger Authors.
// SPDX-License-Identifier: Apache-2.0

package app

import (
	"fmt"

	"go.opentelemetry.io/collector/pdata/ptrace"

	"github.com/jaegertracing/jaeger-idl/model/v1"
	"github.com/jaegertracing/jaeger/internal/storage/v2/v1adapter"
)

func otlp2traces(otlpSpans []byte) ([]*model.Trace, error) {
	ptraceUnmarshaler := ptrace.JSONUnmarshaler{}
	otlpTraces, err := ptraceUnmarshaler.UnmarshalTraces(otlpSpans)
	if err != nil {
		return nil, fmt.Errorf("cannot unmarshal OTLP : %w", err)
	}
	jaegerBatches := v1adapter.V1BatchesFromTraces(otlpTraces)
	var traces []*model.Trace
	traceMap := make(map[model.TraceID]*model.Trace)
	for _, batch := range jaegerBatches {
		for _, span := range batch.Spans {
			if span.Process == nil {
				span.Process = batch.Process
			}
			trace, ok := traceMap[span.TraceID]
			if !ok {
				newtrace := model.Trace{
					Spans: []*model.Span{span},
				}
				traceMap[span.TraceID] = &newtrace
				traces = append(traces, &newtrace)
			} else {
				trace.Spans = append(trace.Spans, span)

View on GitHub (pinned to 806f444784)

Solutions

  1. Inspect the wrapped inner error (%w) to identify the exact JSON/OTLP decode failure and fix the payload accordingly.
  2. Validate the payload is real OTLP JSON (ExportTraceServiceRequest shape, hex-encoded traceId/spanId, proper resourceSpans/scopeSpans nesting) before sending.
  3. If sending Jaeger-format JSON, send it to the Jaeger-native endpoint instead of the OTLP one.
  4. Check that client and server OTLP schema versions are compatible; regenerate payloads from a current SDK/exporter.

Example fix

// before: sending Jaeger-style JSON to the OTLP translator
POST body: {"data":[{"traceID":"abc","spans":[...]}]}

// after: correct OTLP JSON shape
{"resourceSpans":[{"resource":{"attributes":[...]},"scopeSpans":[{"spans":[{"traceId":"0af7651916cd43dd8448eb211c80319c","spanId":"b7ad6b7169203331","name":"op","kind":"SPAN_KIND_SERVER"}]}]}]}
Defensive patterns

Strategy: validation

Validate before calling

var buf map[string]any
if err := json.Unmarshal(otlpSpans, &buf); err != nil {
    return fmt.Errorf("payload is not valid JSON: %w", err)
}
if _, ok := buf["resourceSpans"]; !ok {
    return errors.New("payload lacks resourceSpans; not an OTLP ExportTraceServiceRequest")
}

Type guard

func isOTLPJSON(body []byte) bool {
    var m map[string]json.RawMessage
    return json.Unmarshal(body, &m) == nil && m["resourceSpans"] != nil
}

Try / catch

traces, err := otlp2traces(otlpSpans)
if err != nil {
    var syntaxErr *json.SyntaxError
    if errors.As(err, &syntaxErr) {
        return fmt.Errorf("malformed OTLP JSON at offset %d: %w", syntaxErr.Offset, err)
    }
    return fmt.Errorf("OTLP decode rejected: %w", err)
}

Prevention

When it happens

Trigger: transformOTLP -> otlp2traces is invoked with a []byte payload that fails ptrace.JSONUnmarshaler.UnmarshalTraces: syntactically invalid JSON, JSON that is not an OTLP ExportTraceServiceRequest-compatible structure, missing required fields such as resourceSpans, or wrong field types.

Common situations: A client posts a Jaeger JSON payload (or arbitrary JSON) to an endpoint that expects OTLP JSON; a span field has the wrong type (e.g. traceId as integer instead of hex string); empty or truncated request bodies; version drift where the payload uses an OTLP field name the embedded OTel collector version does not recognize.

Related errors


AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01). Data as JSON: /api/errors/22d992d22d6d34fd. Report an issue: GitHub.