jaegertracing/jaeger · error

trace ID must be 32 hex characters, got %d

Error message

trace ID must be 32 hex characters, got %d

What it means

parseTraceID is the low-level helper behind the "invalid trace_id" wrap: it enforces that a trace ID string is exactly 32 hex characters (16 bytes / 128 bits), the W3C Trace Context width Jaeger uses. When the length check fails it emits this message with the actual length; a subsequent hex decode failure yields a separate "invalid hex string" error.

Source

Thrown at cmd/jaeger/internal/extension/jaegerquery/internal/mcptools/internal/handlers/get_span_details.go:275

		}
		return result
	case pcommon.ValueTypeMap:
		m := v.Map()
		result := make(map[string]any)
		for k, v := range m.All() {
			result[k] = convertAttributeValue(v)
		}
		return result
	default:
		return nil
	}
}

// parseTraceID parses a trace ID string into a pcommon.TraceID.
func parseTraceID(traceIDStr string) (pcommon.TraceID, error) {
	// Parse hex string - TraceID is 16 bytes (32 hex characters)
	if len(traceIDStr) != 32 {
		return pcommon.TraceID{}, fmt.Errorf("trace ID must be 32 hex characters, got %d", len(traceIDStr))
	}

	var traceID pcommon.TraceID
	bytes, err := hex.DecodeString(traceIDStr)
	if err != nil {
		return pcommon.TraceID{}, fmt.Errorf("invalid hex string: %w", err)
	}

	copy(traceID[:], bytes)
	return traceID, nil
}

// parseSpanID parses a span ID string into a pcommon.SpanID.
func parseSpanID(spanIDStr string) (pcommon.SpanID, error) {
	// Parse hex string - SpanID is 8 bytes (16 hex characters)
	if len(spanIDStr) != 16 {
		return pcommon.SpanID{}, fmt.Errorf("span ID must be 16 hex characters, got %d", len(spanIDStr))
	}

View on GitHub (pinned to 806f444784)

Solutions

  1. Ensure the string is exactly 32 characters before calling; left-pad with zeros if it is 16.
  2. Normalize: strip 0x, dashes, whitespace, and re-check len == 32.
  3. Keep IDs as canonical lowercase hex throughout your pipeline.
  4. Wrap your own calls with a length check and pad rather than passing through raw user/log output.

Example fix

// before
id := traceIDString() // "e8d3fbe4e0b6e0f2" (16 chars)
parseTraceID(id) // -> "trace ID must be 32 hex characters, got 16"
// after
id := fmt.Sprintf("%032s", traceIDString())
parseTraceID(id) // ok
Defensive patterns

Strategy: validation

Validate before calling

func ensureTraceID(s string) (string, error) {
	s = strings.ToLower(strings.TrimPrefix(strings.TrimSpace(s), "0x"))
	if len(s) == 16 { s = strings.Repeat("0", 16) + s }
	if len(s) != 32 {
		return "", fmt.Errorf("trace ID must be 32 hex characters, got %d", len(s))
	}
	if _, err := hex.DecodeString(s); err != nil { return "", err }
	return s, nil
}

Type guard

func is32Hex(s string) bool {
	return len(s) == 32 && func() bool { _, err := hex.DecodeString(s); return err == nil }()
}

Try / catch

id, err := ensureTraceID(raw)
if err != nil {
	if strings.Contains(err.Error(), "must be 32 hex characters") {
		raw = fmt.Sprintf("%032s", strings.TrimPrefix(raw, "0x"))
		id, err = ensureTraceID(raw)
	}
}

Prevention

When it happens

Trigger: Any call path reaching parseTraceID (buildQuery for span details, or the anonymous wrapper) with a trace ID string whose len != 32 — e.g. 16-char legacy IDs, IDs with a 0x prefix or dashes altering length, or truncated IDs.

Common situations: W3C 8-byte trace IDs from instrumented apps stored unpadded; string manipulation that truncated or concatenated IDs; IDs obtained from OTLP JSON with different encoding.

Related errors


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