jaegertracing/jaeger · error

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

Error message

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

What it means

decodeSpanID converts a hex string into a fixed 8-byte pcommon.SpanID. This error is thrown when the hex string decodes but the resulting byte slice is not exactly 8 bytes. The strict length check ensures a valid pcommon.SpanID can always be populated.

Source

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

		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
}

func convertSpan(sr *SpanRow) (ptrace.Span, error) {
	span := ptrace.NewSpan()
	span.SetStartTimestamp(pcommon.NewTimestampFromTime(sr.StartTime))
	traceId, err := decodeTraceID(sr.TraceID)
	if err != nil {
		return span, fmt.Errorf("failed to decode trace ID: %w", err)
	}
	span.SetTraceID(traceId)
	spanId, err := decodeSpanID(sr.ID)
	if err != nil {
		return span, fmt.Errorf("failed to decode span ID: %w", err)
	}
	span.SetSpanID(spanId)

View on GitHub (pinned to 806f444784)

Solutions

  1. Confirm span IDs stored in ClickHouse are exactly 16 hex characters (8 bytes)
  2. Check the writer path that serializes span IDs to ensure it emits full-length hex
  3. Fix or drop the malformed rows
  4. Add a validation step at ingestion to reject non-8-byte span IDs before they reach storage

Example fix

// before: 12-hex-char span id fails
// after: validate before persisting
if len(spanIDHex) != 16 { return fmt.Errorf("span ID must be 16 hex chars, got %d", len(spanIDHex)) }
Defensive patterns

Strategy: validation

Validate before calling

func validSpanIDHex(s string) bool {
    b, err := hex.DecodeString(s)
    return err == nil && len(b) == 8
}
// before FromRow: if !validSpanIDHex(row.ID) { handle }

Type guard

func isSpanID(s string) (pcommon.SpanID, bool) {
    var id pcommon.SpanID
    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)
var decodeErr *fmt.wrapError
if errors.As(err, &decodeErr) && strings.Contains(err.Error(), "span ID") {
    log.Warn("malformed span ID, skipping row", "err", err)
} else if err != nil {
    return err
}

Prevention

When it happens

Trigger: Calling FromRow/convertSpan with a SpanRow whose ID or ParentSpanID column decodes to a byte length other than 8, e.g. an empty span ID, a 16-hex-char (8-byte) value stored truncated/padded differently, or oversized strings.

Common situations: Data written by another tooling version with different span-ID widths; manual SQL edits; corrupted rows from a bad ETL job.

Related errors


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