Input Recursion Limit

Goal Input Output Handling Frequency Occasional Category Operations Published View source on GitHub ↗

Issue

An agent’s parser (JSON, XML, YAML, or a custom nested-structure format) receives an input with excessive nesting depth — either from a legitimately complex source, a buggy upstream serializer that loops, or a deliberately crafted payload — and the recursive-descent parsing logic exceeds the language runtime’s call-stack limit or the parser’s own recursion guard, crashing the process rather than rejecting the input gracefully. Because the crash happens inside the parsing library itself, it often takes down the whole request-handling worker rather than failing just the one bad input.

Frequency: Occasional

Symptoms

  • Worker process crashes with a stack-overflow or RecursionError traceback pointing into a JSON/XML/YAML parsing library
  • A single malformed or deeply-nested input takes down an entire request-handling process, affecting concurrent unrelated requests
  • Crash is intermittent and hard to reproduce because it depends on the specific input’s nesting depth and the runtime’s current stack usage
  • No graceful error response reaches the caller — the connection simply drops or times out
  • Retrying the same request deterministically reproduces the crash, unlike most transient failures

Root Cause

Most general-purpose parsers for nested formats (JSON, XML, YAML) are implemented as recursive-descent parsers for simplicity, where each level of nesting in the input corresponds to one additional stack frame in the parser. Language runtimes impose a maximum call-stack depth (a few thousand frames is typical), and unless the parser explicitly tracks and caps nesting depth as a first check, the recursive calls will exceed that runtime limit before any input-validation logic gets a chance to reject the input as “too deeply nested.” The failure is a raw runtime exception (stack overflow, RecursionError, or in unmanaged runtimes, a hard crash) rather than a caught, application-level validation error, which is why it typically bypasses normal error-handling paths.

Example

An agent ingests JSON configuration payloads submitted via an API and
parses them with a standard recursive-descent JSON library configured
with no explicit depth limit.

A misbehaving upstream client has a serialization bug that, under a rare
condition, wraps a config value in a self-referential-looking nested
array structure 50,000 levels deep (a legitimate structure was meant to
be 3 levels deep, but a loop counter bug caused re-wrapping on every
retry attempt for six hours before anyone noticed).

The agent's parser begins recursing into the structure. At roughly frame
3,000-8,000 (depending on the runtime and current stack usage), the
process hits its stack limit and crashes with a stack overflow. Because
this happens inside a shared worker process handling multiple concurrent
requests, all in-flight requests on that worker are dropped, not just the
one with the bad payload. The process restarts, the same payload is
retried by the client's retry logic, and the worker crashes again in a
loop until the payload is manually purged from the retry queue.

Statistics

FindingContext
Recursive-descent parsers without an explicit depth cap typically fail somewhere between 1,000 and 10,000 levels of nesting, depending on runtime and stack sizeGeneral characteristic of common parsing libraries and default stack sizes
A small but recurring share of parser-related production crashes in agent input pipelines trace to unbounded nesting rather than malformed syntaxTypical range observed in crash-report triage
Adding an explicit max-depth check before or during parsing eliminates this crash class with negligible performance costEstimated from the low cost of a depth counter relative to parse time

Mitigations

  1. Explicit depth limits: Configure or wrap the parser with an explicit maximum nesting depth (e.g. 32 or 64 levels, well within realistic legitimate use) and reject anything deeper with a normal validation error before recursion approaches the runtime’s stack limit.
  2. Iterative parsing for untrusted input: For input sources that are untrusted or externally controlled, prefer an iterative (explicit-stack) parser implementation over a recursive-descent one, so depth is bounded by available heap memory and an application-level check rather than the call stack.
  3. Process isolation for parsing untrusted payloads: Parse untrusted or externally-sourced structured input in an isolated worker/sandbox so a stack-overflow crash doesn’t take down concurrent unrelated requests on the same process.
  4. Pre-parse size and structural sanity checks: Reject payloads exceeding a reasonable raw size or bracket/tag-depth count via a cheap pre-scan before handing them to the full parser.
  5. Upstream serializer bug monitoring: Track nesting-depth distribution of legitimate payloads from each source over time, and alert when a source’s typical depth suddenly spikes, indicating a serialization bug rather than a change in legitimate data shape.

Production Signals

Key Metrics

MetricDescriptionAlert Threshold
parser_crash_rateRate of worker crashes originating inside structured-input parsing codeAlert if > 0 sustained
input_nesting_depth_p9999th percentile nesting depth of accepted structured inputsAlert if p99 approaches the configured max-depth limit
depth_limit_rejection_countCount of inputs rejected for exceeding the configured max nesting depthAlert on sudden spike (possible attack or upstream bug)

Alerts

AlertConditionSeverityResponse
Worker crash in parserA request-handling process crashes with a stack-overflow/RecursionError inside a parsing libraryHighIsolate and quarantine the triggering payload, verify depth limits are configured, restart affected workers
Nesting depth spike from a known sourceA specific upstream source’s payload nesting depth deviates sharply from its historical baselineMediumContact source owner, inspect for a serialization bug
  • Input Size Not Validated - both are failures to bound an input’s cost (depth vs. raw size) before the parser fully commits to processing it
  • Input Schema Evolution - an upstream serialization bug causing runaway nesting is a specific case of unexpected structural drift
  • Output Truncation Silent - a related failure where an oversized structure causes silent data loss rather than a crash