{"record":{"id":"4315e8062f2240b8","repo":"JuliusBrussee/caveman","slug":"mapped-span-id-is-empty","errorCode":null,"errorMessage":"mapped span_id is empty","messagePattern":"mapped span_id is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/importers/generic.go","lineNumber":162,"sourceCode":"\t\t\tassignInt(&sp, target, parseFlexInt(val))\n\t\tcase \"float\":\n\t\t\tsp.TotalCostUSD = roundUSD(parseFlexFloat(val))\n\t\tcase \"time\":\n\t\t\ts, ns := parseTimeFlexible(val)\n\t\t\tif target == \"timestamp\" {\n\t\t\t\tsp.Timestamp = s\n\t\t\t\tstartNs = ns\n\t\t\t} else {\n\t\t\t\tsp.EndTimestamp = s\n\t\t\t\tendNs = ns\n\t\t\t}\n\t\t}\n\t}\n\tif strings.TrimSpace(sp.TraceID) == \"\" {\n\t\treturn Span{}, nil, fmt.Errorf(\"mapped trace_id is empty\")\n\t}\n\tif strings.TrimSpace(sp.SpanID) == \"\" {\n\t\treturn Span{}, nil, fmt.Errorf(\"mapped span_id is empty\")\n\t}\n\tif sp.Timestamp == \"\" {\n\t\treturn Span{}, nil, fmt.Errorf(\"mapped timestamp is missing or invalid\")\n\t}\n\tif d := durationMS(startNs, endNs); d > 0 {\n\t\tsp.DurationMS = d\n\t}\n\tsp.Attributes = map[string]string{\"import.source\": \"generic\"}\n\tapplyScope(&sp, opts)\n\treturn sp, resolved, nil\n}\n\nfunc assignString(sp *Span, target, v string) {\n\tswitch target {\n\tcase \"trace_id\":\n\t\tsp.TraceID = v\n\tcase \"span_id\":\n\t\tsp.SpanID = v","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/importers/generic.go#L144-L180","documentation":"Same final validation as 1183 but for SpanID: the mapped span_id path resolved, yet the resulting string is empty after trimming. Every span needs a non-empty span ID because it is the primary identifier in caveman.spans. The check runs after trace_id, so trace ID was valid on this record.","triggerScenarios":"Record has span_id present but empty/null/whitespace; span ID stored under a different populated field while the mapped one is a placeholder; asString of a non-string value (object, array) yielding an empty result.","commonSituations":"Exports where parent-only or synthetic rows carry an empty span ID; partial renames in the source schema leaving the old key present but blank; mapping span_id to a field that only exists on child spans.","solutions":["Inspect the failing record and populate or drop it upstream","Re-point the span_id mapping at the field that actually holds a non-empty value on every record","Synthesize span IDs during pre-processing if the source genuinely lacks them","Verify the mapping is not pointing at trace_id's field on records where both coincide"],"exampleFix":"// before\nrec := map[string]any{\"traceId\": \"t1\", \"spanId\": nil, \"started_at\": \"2026-01-01T00:00:00Z\"}\n\n// after\nrec := map[string]any{\"traceId\": \"t1\", \"spanId\": \"s1\", \"started_at\": \"2026-01-01T00:00:00Z\"}","handlingStrategy":"validation","validationCode":"func hasNonEmptySpanID(rec map[string]any, path string) bool {\n\tv, ok := lookupPath(rec, path)\n\tif !ok { return false }\n\ts, ok := v.(string)\n\treturn ok && strings.TrimSpace(s) != \"\"\n}","typeGuard":"func isUsableID(v any) bool {\n\ts, ok := v.(string)\n\treturn ok && strings.TrimSpace(s) != \"\"\n}","tryCatchPattern":"if strings.Contains(err.Error(), \"span_id is empty\") { /* drop or generate span ID for that record */ }","preventionTips":["Filter records with blank span IDs before import","Generate span IDs (UUID) in pre-processing when the source omits them","Assert non-empty span IDs in the exporting system's schema"],"tags":["go","import","generic","data-quality"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}