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
- Confirm span IDs stored in ClickHouse are exactly 16 hex characters (8 bytes)
- Check the writer path that serializes span IDs to ensure it emits full-length hex
- Fix or drop the malformed rows
- 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
- Enforce 16-hex-char span IDs in all writers
- Reject empty or placeholder span IDs at ingestion
- Add a schema/data quality check that scans for malformed IDs
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
- failed to decode span ID: %w
- invalid length %d of decoded trace ID %q, expected %d bytes
- failed to decode parent span ID: %w
- span ID must not be all zero
- ttl must be a non-negative duration
AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01).
Data as JSON: /api/errors/bf00e5ed57f3d00d.
Report an issue: GitHub.