{"record":{"id":"dfe4f6ed0d89dff8","repo":"JuliusBrussee/caveman","slug":"duplicate-object-key-q","errorCode":null,"errorMessage":"duplicate object key %q","messagePattern":"duplicate object key %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/compressors/toon_encode.go","lineNumber":144,"sourceCode":"\t\treturn toonValue{}, err\n\t}\n\tswitch t := tok.(type) {\n\tcase json.Delim:\n\t\tswitch t {\n\t\tcase '{':\n\t\t\tvar fields []toonField\n\t\t\tseen := make(map[string]struct{})\n\t\t\tfor dec.More() {\n\t\t\t\tkeyTok, err := dec.Token()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn toonValue{}, err\n\t\t\t\t}\n\t\t\t\tkey, ok := keyTok.(string)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn toonValue{}, fmt.Errorf(\"object key is %T\", keyTok)\n\t\t\t\t}\n\t\t\t\tif _, duplicate := seen[key]; duplicate {\n\t\t\t\t\treturn toonValue{}, fmt.Errorf(\"duplicate object key %q\", key)\n\t\t\t\t}\n\t\t\t\tseen[key] = struct{}{}\n\t\t\t\tval, err := readJSONTOONValue(dec)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn toonValue{}, err\n\t\t\t\t}\n\t\t\t\tfields = append(fields, toonField{name: key, value: val})\n\t\t\t}\n\t\t\tend, err := dec.Token()\n\t\t\tif err != nil {\n\t\t\t\treturn toonValue{}, err\n\t\t\t}\n\t\t\tif d, ok := end.(json.Delim); !ok || d != '}' {\n\t\t\t\treturn toonValue{}, fmt.Errorf(\"object not closed\")\n\t\t\t}\n\t\t\treturn toonValue{kind: toonObject, obj: fields}, nil\n\t\tcase '[':\n\t\t\tvar arr []toonValue","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/engine/compressors/toon_encode.go#L126-L162","documentation":"readJSONTOONValue tracks keys already seen in an object and rejects duplicates with \"duplicate object key %q\". encoding/json's Decode into a map would silently keep the last value; TOON's strict parser fails closed so lossless round-tripping is guaranteed.","triggerScenarios":"parseJSONTOON encounters raw JSON text containing the same key twice in one object, e.g. {\"a\":1,\"a\":2}, whether handwritten, concatenated, or produced by a template.","commonSituations":"Handwritten JSON with copy-pasted keys; naive string concatenation of JSON fragments; generated JSON from tools that don't dedupe keys; semantically-duplicate keys differing only in escaping.","solutions":["Remove the duplicate key from the JSON, keeping the intended value.","Normalize input with `jq -c ' .'` style preprocessing — note jq also last-wins, so decide the winner explicitly first.","Fix the upstream producer to emit unique keys.","If duplicates are intentional, keep JSON — TOON refuses lossy input by design."],"exampleFix":"// before\necho '{\"a\":1,\"a\":2}' | caveman-engine toon\n// after\necho '{\"a\":2}' | caveman-engine toon","handlingStrategy":"validation","validationCode":"func hasDuplicateKeys(input []byte) bool {\n    dec := json.NewDecoder(bytes.NewReader(input))\n    // walk tokens tracking keys per object depth; return true on repeat\n    return walkAndDetectDupKeys(dec)\n}","typeGuard":null,"tryCatchPattern":"v, err := parseJSONTOON(input)\nif err != nil && strings.Contains(err.Error(), \"duplicate object key\") {\n    return fmt.Errorf(\"dedupe JSON keys before TOON: %w\", err)\n}","preventionTips":["Lint JSON for duplicate keys before sending to the engine","Never concatenate JSON fragments as strings","Make upstream generators emit unique keys"],"tags":["toon","json","duplicate-keys","validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}