{"record":{"id":"b564505c7555bd65","repo":"JuliusBrussee/caveman","slug":"generic-decode-expected-a-json-array-or-data","errorCode":null,"errorMessage":"generic decode: expected a JSON array or {\"data\":[...]} of objects","messagePattern":"generic decode: expected a JSON array or (.+?) of objects","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/importers/generic.go","lineNumber":123,"sourceCode":"\t\t}\n\t}\n\treturn nil\n}\n\nfunc decodeGenericRecords(data []byte) ([]map[string]any, error) {\n\t// Try a bare array first.\n\tvar arr []map[string]any\n\tif err := json.Unmarshal(data, &arr); err == nil {\n\t\treturn arr, nil\n\t}\n\t// Then a {\"data\":[...]} wrapper.\n\tvar env struct {\n\t\tData []map[string]any `json:\"data\"`\n\t}\n\tif err := json.Unmarshal(data, &env); err == nil && env.Data != nil {\n\t\treturn env.Data, nil\n\t}\n\treturn nil, fmt.Errorf(\"generic decode: expected a JSON array or {\\\"data\\\":[...]} of objects\")\n}\n\nfunc mapGeneric(rec map[string]any, opts Options) (Span, map[string]bool, error) {\n\tvar sp Span\n\tstartNs, endNs := int64(0), int64(0)\n\tresolved := make(map[string]bool, len(opts.FieldMap))\n\tfor target, path := range opts.FieldMap {\n\t\tkind := genericTargets[target]\n\t\tval, found := lookupPath(rec, path)\n\t\tif !found {\n\t\t\tif target == \"trace_id\" || target == \"span_id\" || target == \"timestamp\" {\n\t\t\t\treturn Span{}, nil, fmt.Errorf(\"mapped source path %q for required target %q was not found\", path, target)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tresolved[target] = true\n\t\tswitch kind {\n\t\tcase \"string\":","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/importers/generic.go#L105-L141","documentation":"decodeGenericRecords accepts exactly two payload shapes: a top-level JSON array of objects, or an object with a \"data\" key holding an array. If json.Unmarshal fails for both shapes (or the wrapper parsed but env.Data is nil), this error is returned. It means the input bytes are not valid JSON for either accepted envelope, so mapping is never attempted.","triggerScenarios":"Feeding a JSON object like {\"rows\":[...]} or {\"observations\":[...]} (wrong wrapper key); truncated/malformed JSON; a JSONL file (newline-delimited objects) passed to the generic importer, which expects one document; a top-level JSON scalar, string, or an array of non-object elements.","commonSituations":"Exporting from a vendor dashboard that wraps results in a non-standard key; piping curl output that got cut off; assuming JSONL is supported because the caveman-jsonl format exists; a UTF-8 BOM prepended by Windows tooling breaking the first Unmarshal.","solutions":["Re-shape the payload to a bare array [...] or {\"data\":[...]} and re-import","If the wrapper key is different (e.g. \"rows\"), unwrap it in your own code before passing bytes to the generic importer","For newline-delimited JSON, use the caveman-jsonl format instead of generic","Validate the file parses as JSON and starts with '[' or '{' before importing"],"exampleFix":"// before\nraw := []byte(`{\"rows\":[{\"traceId\":\"t1\"}]}`)\nrows, sum, err := importers.Import(importers.FormatGeneric, raw, opts)\n\n// after\nraw := []byte(`{\"data\":[{\"traceId\":\"t1\"}]}`)\nrows, sum, err := importers.Import(importers.FormatGeneric, raw, opts)","handlingStrategy":"validation","validationCode":"func looksLikeGenericPayload(data []byte) bool {\n\td := bytes.TrimSpace(data)\n\treturn len(d) > 0 && (d[0] == '[' || (d[0] == '{' && bytes.Contains(bytes.ToLower(d), []byte(\"\\\"data\\\"\"))))\n}","typeGuard":null,"tryCatchPattern":"if _, _, err := importers.Import(importers.FormatGeneric, raw, opts); err != nil { if strings.HasPrefix(err.Error(), \"generic decode\") { return fmt.Errorf(\"input file is not a generic JSON export: %w\", err) } }","preventionTips":["Run a json.Valid check and inspect the first non-space byte before importing","Standardize exports on the {\"data\":[...]} wrapper in your pipeline","Use caveman-jsonl for newline-delimited files, never generic"],"tags":["go","import","generic","json","decode"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}