JuliusBrussee/caveman · error
cacheengine: duplicate or invalid object key
Error message
cacheengine: duplicate or invalid object key
What it means
Thrown by inspectUniqueJSONValue while iterating an object's members when a key token is not a JSON string (impossible in valid JSON, so effectively a decoder-state bug) or when the same key appears twice in one object. The engine enforces key uniqueness because duplicate keys make cache-marker path matching ambiguous.
Source
Thrown at cacheengine/native.go:495
delim, composite := token.(json.Delim)
if !composite {
if root {
return false, errors.New("cacheengine: request root must be object")
}
return false, nil
}
switch delim {
case '{':
seen := map[string]bool{}
found := false
for decoder.More() {
keyToken, err := decoder.Token()
if err != nil {
return false, err
}
key, ok := keyToken.(string)
if !ok || seen[key] {
return false, errors.New("cacheengine: duplicate or invalid object key")
}
seen[key] = true
matched := cacheMarkerAt(provider, path, key)
path = append(path, key)
childFound, err := inspectUniqueJSONValue(decoder, false, depth+1, provider, path)
path = path[:len(path)-1]
if err != nil {
return false, err
}
found = found || matched || childFound
}
closing, err := decoder.Token()
if err != nil || closing != json.Delim('}') {
return false, errors.New("cacheengine: invalid object close")
}
return found, nil
case '[':
if root {View on GitHub (pinned to 27d5a3981a)
Solutions
- Decode conflicting JSONs into a map, merge, then re-marshal so keys are unique
- Stop appending serialized fragments to an existing body string; build the object once
- If injecting a cache marker, check the body does not already contain that key
Example fix
// before body := rawBody + ",\"cache_marker\":\"x\"}" // rawBody may already contain cache_marker // after var m map[string]any json.Unmarshal(rawBodyBytes, &m) m["cache_marker"] = "x" body, _ := json.Marshal(m)
Defensive patterns
Strategy: validation
Validate before calling
var probe map[string]json.RawMessage
if err := json.Unmarshal(body, &probe); err != nil { return err } // duplicate keys collapse silently but at least proves validity; for strictness use a token scanner that tracks seen keys per object Type guard
// n/a
Prevention
- Never merge JSON by string concatenation; decode, merge maps, re-marshal
- Check for an existing key before injecting markers
When it happens
Trigger: A body containing an object with a repeated key, e.g. {"model":"a", ..., "model":"b"} — some JSON writers (hand-built string concatenation, lenient serializers, naive map merging) emit duplicates; Go's encoding/json cannot produce them from a map, but manual construction or upstream proxies can.
Common situations: Merging two request JSONs by string concatenation instead of decoding/merging maps; hand-rolled JSON templating in scripts or tests; a middleware that appends a field (e.g. a cache marker) to an already-serialized body that contains it.
Related errors
- cacheengine: request root must be object
- cacheengine: invalid object close
- cacheengine: invalid array close
- cacheengine: JSON nesting limit exceeded
- cacheengine: unexpected delimiter
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/ea4165c08c180dc5.
Report an issue: GitHub.