{"record":{"id":"cd5b916502fc1a70","repo":"JuliusBrussee/caveman","slug":"otlp-decode-w","errorCode":null,"errorMessage":"otlp decode: %w","messagePattern":"otlp decode: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/importers/otlp.go","lineNumber":89,"sourceCode":"}\n\ntype otlpEvent struct {\n\tName         string   `json:\"name\"`\n\tTimeUnixNano string   `json:\"timeUnixNano\"`\n\tAttributes   []otlpKV `json:\"attributes\"`\n}\n\ntype otlpStatus struct {\n\tCode    int    `json:\"code\"`\n\tMessage string `json:\"message\"`\n}\n\n// parseOTLP decodes an OTLP/JSON trace payload and maps GenAI semantic\n// conventions to Span columns. On a JSON error it returns no rows.\nfunc parseOTLP(data []byte, opts Options) ([]Span, error) {\n\tvar p otlpPayload\n\tif err := json.Unmarshal(data, &p); err != nil {\n\t\treturn nil, fmt.Errorf(\"otlp decode: %w\", err)\n\t}\n\tvar rows []Span\n\tfor _, rs := range p.ResourceSpans {\n\t\tresAttrs := otlpKVMap(rs.Resource.Attributes)\n\t\tfor _, ss := range rs.ScopeSpans {\n\t\t\tfor _, sp := range ss.Spans {\n\t\t\t\trows = append(rows, mapOTLPSpan(sp, resAttrs, opts))\n\t\t\t}\n\t\t}\n\t}\n\treturn rows, nil\n}\n\nfunc mapOTLPSpan(sp otlpSpan, resAttrs map[string]string, opts Options) Span {\n\tattrs := otlpKVMap(sp.Attributes)\n\n\tgenAI := telemetry.ExtractGenAIFields(attrs, resAttrs, sp.Name)\n","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/importers/otlp.go#L71-L107","documentation":"parseOTLP unmarshals the payload into a strictly typed otlpPayload (OTLP/JSON trace export shape: resourceSpans -> scopeSpans -> spans). Any JSON error is wrapped as 'otlp decode: %w'. Note the doc comment: on a JSON error it returns no rows — partial data is never emitted. Type mismatches on nested OTLP fields (e.g. traceId expected as hex string but given object) surface here too.","triggerScenarios":"Feeding OTLP/proto (binary) bytes to a JSON parser; a truncated export; passing an OTLP metrics or logs export instead of traces; an exporter emitting non-standard JSON where a span field has the wrong type; feeding the generic {'data':...} shape to the otlp format.","commonSituations":"Confusing content-type between protobuf and JSON OTLP encodings on the collector; version skew with pre-release OTLP JSON schemas; proxy or LB truncating large trace batches; testing with a hand-written JSON file missing required nesting.","solutions":["Confirm the payload is OTLP/JSON traces (resourceSpans at the root), not protobuf or another signal type","Read the wrapped json error for the offending offset/field and validate the document against the OTLP JSON schema","If exporting from a collector, set exporters to JSON encoding explicitly","Re-fetch or re-export the batch if truncation is suspected"],"exampleFix":"// before\nrows, sum, err := importers.Import(importers.FormatOTLP, protoBytes, opts) // binary OTLP\n\n// after\njsonBytes, _ := protoToJSONOtlp(protoBytes) // encode via collector or protojson\nrows, sum, err := importers.Import(importers.FormatOTLP, jsonBytes, opts)","handlingStrategy":"validation","validationCode":"func looksLikeOTLPJSON(data []byte) bool {\n\td := bytes.TrimSpace(data)\n\treturn len(d) > 0 && d[0] == '{' && bytes.Contains(d, []byte(\"resourceSpans\"))\n}","typeGuard":null,"tryCatchPattern":"if _, _, err := importers.Import(importers.FormatOTLP, raw, opts); err != nil { if strings.Contains(err.Error(), \"otlp decode\") { return fmt.Errorf(\"payload is not OTLP/JSON traces: %w\", err) } }","preventionTips":["Configure OTLP exporters to JSON encoding explicitly in the collector","Never feed protobuf OTLP bytes to the JSON importer; convert first","Smoke-test one batch with a JSON schema check before bulk import"],"tags":["go","import","otlp","json","decode"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}