JuliusBrussee/caveman · error
duplicate object key
Error message
duplicate object key %q
What it means
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.
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.
Example fix
// before
echo '{"a":1,"a":2}' | caveman-engine toon
// after
echo '{"a":2}' | caveman-engine toon Defensive patterns
Strategy: validation
Validate before calling
func hasDuplicateKeys(input []byte) bool {
dec := json.NewDecoder(bytes.NewReader(input))
// walk tokens tracking keys per object depth; return true on repeat
return walkAndDetectDupKeys(dec)
} Try / catch
v, err := parseJSONTOON(input)
if err != nil && strings.Contains(err.Error(), "duplicate object key") {
return fmt.Errorf("dedupe JSON keys before TOON: %w", err)
} Prevention
- Lint JSON for duplicate keys before sending to the engine
- Never concatenate JSON fragments as strings
- Make upstream generators emit unique keys
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- not losslessly TOON-encodable (invalid JSON, or…
- not losslessly TOON-encodable (invalid JSON, or…
- array not closed
- assembly slot content is not JSON-serializable
- cave_output_schema_mismatch
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/dfe4f6ed0d89dff8.
Report an issue: GitHub.
Appendix: source
Thrown at engine/compressors/toon_encode.go:144
return toonValue{}, err
}
switch t := tok.(type) {
case json.Delim:
switch t {
case '{':
var fields []toonField
seen := make(map[string]struct{})
for dec.More() {
keyTok, err := dec.Token()
if err != nil {
return toonValue{}, err
}
key, ok := keyTok.(string)
if !ok {
return toonValue{}, fmt.Errorf("object key is %T", keyTok)
}
if _, duplicate := seen[key]; duplicate {
return toonValue{}, fmt.Errorf("duplicate object key %q", key)
}
seen[key] = struct{}{}
val, err := readJSONTOONValue(dec)
if err != nil {
return toonValue{}, err
}
fields = append(fields, toonField{name: key, value: val})
}
end, err := dec.Token()
if err != nil {
return toonValue{}, err
}
if d, ok := end.(json.Delim); !ok || d != '}' {
return toonValue{}, fmt.Errorf("object not closed")
}
return toonValue{kind: toonObject, obj: fields}, nil
case '[':
var arr []toonValueView on GitHub (pinned to 3ee70a1026)