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

  1. Decode conflicting JSONs into a map, merge, then re-marshal so keys are unique
  2. Stop appending serialized fragments to an existing body string; build the object once
  3. 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

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


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/ea4165c08c180dc5. Report an issue: GitHub.