jaegertracing/jaeger · error

failed to decode parent span ID: %w

Error message

failed to decode parent span ID: %w

What it means

convertSpan wraps failures from decodeSpanID for the ParentSpanID column with this message. Because parent IDs are optional, this only runs when ParentSpanID is non-empty; a non-empty value that is not valid 8-byte hex triggers the error.

Source

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

}

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)
	if sr.ParentSpanID != "" {
		parentSpanId, err := decodeSpanID(sr.ParentSpanID)
		if err != nil {
			return span, fmt.Errorf("failed to decode parent span ID: %w", err)
		}
		span.SetParentSpanID(parentSpanId)
	}
	span.TraceState().FromRaw(sr.TraceState)
	span.SetName(sr.Name)
	span.SetKind(jptrace.StringToSpanKind(sr.Kind))
	span.SetEndTimestamp(pcommon.NewTimestampFromTime(sr.StartTime.Add(time.Duration(sr.Duration))))
	span.Status().SetCode(jptrace.StringToStatusCode(sr.StatusCode))
	span.Status().SetMessage(sr.StatusMessage)

	putAttributes(
		span.Attributes(),
		&sr.Attributes,
		span,
	)

	for i, e := range sr.EventNames {
		event := span.Events().AppendEmpty()

View on GitHub (pinned to 806f444784)

Solutions

  1. Ensure root spans store an empty ParentSpanID, not zeros or placeholders
  2. Normalize parent IDs to exactly 16 hex chars at ingestion
  3. Fix or clear malformed parent_span_id values in ClickHouse
  4. Sanitize legacy data during migration

Example fix

// before: root span with parent "00000000"
sr.ParentSpanID = "00000000"
// after
sr.ParentSpanID = "" // root spans must leave parent ID empty
Defensive patterns

Strategy: validation

Validate before calling

if sr.ParentSpanID != "" {
    if _, ok := isSpanID(sr.ParentSpanID); !ok {
        return fmt.Errorf("parent span ID %q is malformed; use empty string for root spans", sr.ParentSpanID)
    }
}

Type guard

func hasValidParentSpanID(sr *SpanRow) bool {
    if sr.ParentSpanID == "" { return true }
    _, ok := isSpanID(sr.ParentSpanID)
    return ok
}

Try / catch

span, err := FromRow(sr)
if err != nil && strings.Contains(err.Error(), "failed to decode parent span ID") {
    log.Warn("bad parent span ID, treating as root", "parent", sr.ParentSpanID)
    // proceed without parent
}

Prevention

When it happens

Trigger: FromRow encounters a SpanRow with a non-empty ParentSpanID that is invalid hex or not exactly 8 bytes when decoded, e.g. root spans mistakenly given a placeholder parent like "0000" or a 16-byte parent ID.

Common situations: Root spans written with sentinel parent values instead of empty string; legacy data with 8-byte trace IDs used as parent IDs; ETL tools writing padded parent IDs.

Understand the failure class

Related errors


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