JuliusBrussee/caveman · error
cacheengine: JSON nesting limit exceeded
Error message
cacheengine: JSON nesting limit exceeded
What it means
Thrown by inspectUniqueJSONValue when the streaming JSON decoder descends past depth 512 while walking the request body looking for cache markers. It protects the recursive walker (and the JSON codec) from stack exhaustion on adversarial deeply-nested payloads like '[[[[...'.
Source
Thrown at cacheengine/native.go:471
func validUniqueJSONObject(body []byte) bool {
valid, _ := inspectUniqueJSONObject(body, "")
return valid
}
func inspectUniqueJSONObject(body []byte, provider string) (bool, bool) {
decoder := json.NewDecoder(bytes.NewReader(body))
decoder.UseNumber()
found, err := inspectUniqueJSONValue(decoder, true, 0, provider, nil)
if err != nil {
return false, false
}
_, err = decoder.Token()
return errors.Is(err, io.EOF), found
}
func inspectUniqueJSONValue(decoder *json.Decoder, root bool, depth int, provider string, path []string) (bool, error) {
if depth > 512 {
return false, errors.New("cacheengine: JSON nesting limit exceeded")
}
token, err := decoder.Token()
if err != nil {
return false, err
}
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()View on GitHub (pinned to 27d5a3981a)
Solutions
- Reject or bound nesting at your own ingress before the engine sees the body (most servers cap depth well below 512)
- Fix cyclic data structures that produce unbounded nesting when serialized
- Treat the engine's 'not found' result on such bodies as a reject, not a pass
Example fix
// before
body, _ := json.Marshal(payload) // payload contains a reference cycle
// after
// detect cycles before marshal or use a serializer with cycle detection (e.g. 'github.com/...json' with VisitCycleError), and cap nesting:
if nestingDepth(body) > 128 { return errors.New("reject: nesting too deep") } Defensive patterns
Strategy: validation
Validate before calling
func maxNesting(b []byte, limit int) error {
d := json.NewDecoder(bytes.NewReader(b))
depth, max := 0, 0
for {
t, err := d.Token()
if err != nil { return err }
if t == nil { break }
if dl, ok := t.(json.Delim); ok {
if dl == '{' || dl == '[' { depth++; if depth > limit { return fmt.Errorf("nesting %d", depth) } } else { depth-- }
}
}
_ = max
return nil
} Type guard
// n/a
Prevention
- Reject nesting > ~128 at your ingress
- Use cycle-detecting serialization for recursive structures
When it happens
Trigger: A request body whose JSON nesting (objects/arrays) exceeds 512 levels — e.g. thousands of repeated '[' or '{' characters; note the error is returned from the recursive walker and some callers (like the unique-marker scan in native.go:471) discard it and simply treat the body as non-unique/not-found.
Common situations: Adversarial or fuzzed request bodies; accidentally serializing recursive data structures (a struct referencing itself) with a serializer that does not detect cycles; legitimately nested data is never 512 levels deep.
Related errors
- cacheengine: request root must be object
- cacheengine: duplicate or invalid object key
- cacheengine: invalid object close
- cacheengine: invalid array close
- cacheengine: unexpected delimiter
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/e9e74242f199b390.
Report an issue: GitHub.