{"record":{"id":"d107ab935ccaa85a","repo":"jaegertracing/jaeger","slug":"failed-to-decode-parent-span-id-w","errorCode":null,"errorMessage":"failed to decode parent span ID: %w","messagePattern":"failed to decode parent span ID: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/v2/clickhouse/tracestore/dbmodel/from.go","lineNumber":115,"sourceCode":"}\n\nfunc convertSpan(sr *SpanRow) (ptrace.Span, error) {\n\tspan := ptrace.NewSpan()\n\tspan.SetStartTimestamp(pcommon.NewTimestampFromTime(sr.StartTime))\n\ttraceId, err := decodeTraceID(sr.TraceID)\n\tif err != nil {\n\t\treturn span, fmt.Errorf(\"failed to decode trace ID: %w\", err)\n\t}\n\tspan.SetTraceID(traceId)\n\tspanId, err := decodeSpanID(sr.ID)\n\tif err != nil {\n\t\treturn span, fmt.Errorf(\"failed to decode span ID: %w\", err)\n\t}\n\tspan.SetSpanID(spanId)\n\tif sr.ParentSpanID != \"\" {\n\t\tparentSpanId, err := decodeSpanID(sr.ParentSpanID)\n\t\tif err != nil {\n\t\t\treturn span, fmt.Errorf(\"failed to decode parent span ID: %w\", err)\n\t\t}\n\t\tspan.SetParentSpanID(parentSpanId)\n\t}\n\tspan.TraceState().FromRaw(sr.TraceState)\n\tspan.SetName(sr.Name)\n\tspan.SetKind(jptrace.StringToSpanKind(sr.Kind))\n\tspan.SetEndTimestamp(pcommon.NewTimestampFromTime(sr.StartTime.Add(time.Duration(sr.Duration))))\n\tspan.Status().SetCode(jptrace.StringToStatusCode(sr.StatusCode))\n\tspan.Status().SetMessage(sr.StatusMessage)\n\n\tputAttributes(\n\t\tspan.Attributes(),\n\t\t&sr.Attributes,\n\t\tspan,\n\t)\n\n\tfor i, e := range sr.EventNames {\n\t\tevent := span.Events().AppendEmpty()","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/jaegertracing/jaeger/blob/806f4447841ecdb60519f408b004a599d515f437/internal/storage/v2/clickhouse/tracestore/dbmodel/from.go#L97-L133","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure root spans store an empty ParentSpanID, not zeros or placeholders","Normalize parent IDs to exactly 16 hex chars at ingestion","Fix or clear malformed parent_span_id values in ClickHouse","Sanitize legacy data during migration"],"exampleFix":"// before: root span with parent \"00000000\"\nsr.ParentSpanID = \"00000000\"\n// after\nsr.ParentSpanID = \"\" // root spans must leave parent ID empty","handlingStrategy":"validation","validationCode":"if sr.ParentSpanID != \"\" {\n    if _, ok := isSpanID(sr.ParentSpanID); !ok {\n        return fmt.Errorf(\"parent span ID %q is malformed; use empty string for root spans\", sr.ParentSpanID)\n    }\n}","typeGuard":"func hasValidParentSpanID(sr *SpanRow) bool {\n    if sr.ParentSpanID == \"\" { return true }\n    _, ok := isSpanID(sr.ParentSpanID)\n    return ok\n}","tryCatchPattern":"span, err := FromRow(sr)\nif err != nil && strings.Contains(err.Error(), \"failed to decode parent span ID\") {\n    log.Warn(\"bad parent span ID, treating as root\", \"parent\", sr.ParentSpanID)\n    // proceed without parent\n}","preventionTips":["Store empty string (not zeros) for root spans' parent IDs","Normalize parent IDs during data migrations","Reject placeholder parent values at ingestion"],"tags":["clickhouse","parent-span-id","data-migration","hex-decoding"],"backgroundTag":"invalid-span-id-length","analyzedSha":"806f4447841ecdb60519f408b004a599d515f437","analyzedAt":"2026-09-01T02:39:22.140Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}