jaegertracing/jaeger · error

invalid length %d of decoded trace ID %q, expected %d bytes

Error message

invalid length %d of decoded trace ID %q, expected %d bytes

What it means

decodeTraceID converts a hex string from ClickHouse into a 16-byte pcommon.TraceID. This error is thrown when the hex string decodes successfully but does not yield exactly 16 bytes, which is the fixed size of an OpenTelemetry trace ID. The library validates this to guarantee a well-formed pcommon.TraceID can be constructed.

Source

Thrown at internal/storage/v2/clickhouse/tracestore/dbmodel/from.go:78

	putAttributes(
		scope.Attributes(),
		&sr.ScopeAttributes,
		spanForWarnings,
	)

	return scope
}

// decodeTraceID decodes a hex string into a pcommon.TraceID, validating that
// it contains exactly 16 bytes so the conversion cannot panic on corrupted rows.
func decodeTraceID(s string) (pcommon.TraceID, error) {
	var id pcommon.TraceID
	b, err := hex.DecodeString(s)
	if err != nil {
		return id, err
	}
	if len(b) != len(id) {
		return id, fmt.Errorf("invalid length %d of decoded trace ID %q, expected %d bytes", len(b), s, len(id))
	}
	copy(id[:], b)
	return id, nil
}

// decodeSpanID decodes a hex string into a pcommon.SpanID, validating that
// it contains exactly 8 bytes so the conversion cannot panic on corrupted rows.
func decodeSpanID(s string) (pcommon.SpanID, error) {
	var id pcommon.SpanID
	b, err := hex.DecodeString(s)
	if err != nil {
		return id, err
	}
	if len(b) != len(id) {
		return id, fmt.Errorf("invalid length %d of decoded span ID %q, expected %d bytes", len(b), s, len(id))
	}
	copy(id[:], b)
	return id, nil

View on GitHub (pinned to 806f444784)

Solutions

  1. Verify the trace IDs stored in ClickHouse are 32 hex characters (16 bytes) once decoded
  2. Check the ingestion pipeline — the writer must zero-pad or preserve full 16-byte trace IDs
  3. Inspect the offending row(s) and repair or drop malformed trace_id values
  4. If legacy 8-byte IDs exist, migrate them by left-padding with zeros to 16 bytes before storing

Example fix

// before: legacy 8-byte trace id string "abcdef0123456789" (8 bytes) fails validation
// after: pad to 16 bytes before writing/decoding
padded := strings.Repeat("0", 32-len(s)) + s
id, err := decodeTraceID(padded)
Defensive patterns

Strategy: validation

Validate before calling

func validTraceIDHex(s string) bool {
    b, err := hex.DecodeString(s)
    return err == nil && len(b) == 16
}
// before querying: if !validTraceIDHex(row.TraceID) { skip/repair }

Type guard

func isTraceID(s string) (pcommon.TraceID, bool) {
    var id pcommon.TraceID
    b, err := hex.DecodeString(s)
    if err != nil || len(b) != len(id) { return id, false }
    copy(id[:], b)
    return id, true
}

Try / catch

span, err := FromRow(row)
if err != nil {
    if strings.Contains(err.Error(), "invalid length") {
        log.Warn("skipping row with malformed trace ID", "err", err)
        continue
    }
    return err
}

Prevention

When it happens

Trigger: Calling FromRow/convertSpan with a SpanRow whose TraceID column holds a hex string that decodes to fewer or more than 16 bytes, e.g. an empty string, a 32-hex-digit ID from a 16-byte-encoded source that was truncated, or a legacy 8-byte trace ID (16 hex chars).

Common situations: Migrating data from older Jaeger storage where trace IDs were 8 bytes; manual data inserts with malformed IDs; ClickHouse columns typed as String allowing arbitrary-length values.

Related errors


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