pydantic/monty · error · TypeError
Max input depth exceeded
Error message
Max input depth exceeded
What it means
Values passed into the Monty sandbox (inputs, external function results, class instance attrs) are recursively prepared before crossing the wire, and nesting deeper than MAX_INPUT_DEPTH (48) is rejected with this TypeError. The depth cap keeps the JS-side walk from stack overflow and mirrors the authoritative per-shape depth budget the native layer re-checks during encoding.
Source
Thrown at crates/monty-js/ts/classInstance.ts:506
* Outbound walk over a host value heading into the sandbox: replaces
* [`ClassInstance`] wrappers with their wire marker (registering them in
* `store`, eager attrs prepared recursively), recurses into arrays / Maps /
* Sets / plain objects, and rejects any other non-plain object with a
* `TypeError` telling the caller to wrap it.
*/
export function prepare(value: unknown, store: InstanceStore): unknown {
return prepareInner(value, store, 0)
}
/** Recursion guard for the outbound walk itself, so a too-deep value fails
* with a catchable error instead of a `RangeError` mid-recursion. Not the
* authoritative wire budget: the native layer re-checks every value with
* exact per-shape accounting (`exceeds_max_value_depth`) before encoding. */
const MAX_INPUT_DEPTH = 48
function prepareInner(value: unknown, store: InstanceStore, depth: number): unknown {
if (depth > MAX_INPUT_DEPTH) {
throw new TypeError('Max input depth exceeded')
}
if (typeof value !== 'object' || value === null) {
return value
}
const walk = (item: unknown) => prepareInner(item, store, depth + 1)
// `ClassType` and `ClassInstance` are sibling `BaseWrapper`s; the class check simply comes first.
if (value instanceof ClassType) {
return classTypeToMarker(value, store, depth)
}
if (value instanceof ClassInstance) {
return wrapperToMarker(value, store, depth)
}
if (value instanceof MontyClassProxy) {
return value.toMarker(store, depth)
}
if (Array.isArray(value)) {
return walkArray(value, walk)
}View on GitHub (pinned to adc986b362)
Solutions
- Flatten or restructure the value so nesting stays under 48 levels before passing it to the sandbox
- Break deeply linked structures into separate shallow pieces (e.g. pass an array of nodes with references by id) and link them on the Python side
- If the depth is legitimately needed, do the computation on the host and hand the sandbox only the final shallow result
Example fix
// before
session.feedRun('run(data)', { inputs: { data: deeplyNested } });
// after
const flattened = flattenToIdRefs(deeplyNested); // array of shallow nodes
session.feedRun('run(data)', { inputs: { data: flattened } }); Defensive patterns
Strategy: validation
Validate before calling
function depthOf(v, d = 0) {
if (d > 48) return d;
if (typeof v !== 'object' || v === null) return d;
return Math.max(0, ...Object.values(v).map((x) => depthOf(x, d + 1)));
}
if (depthOf(inputs) > 48) throw new Error('inputs exceed 48-level depth'); Try / catch
try {
await session.feedRun(code, { inputs });
} catch (e) {
if (e instanceof TypeError && e.message === 'Max input depth exceeded') {
inputs = flattenToIdRefs(inputs);
await session.feedRun(code, { inputs });
} else throw e;
} Prevention
- Keep values passed to the sandbox shallow; pass id-referenced collections instead of nested trees
- Write a depth-checking helper in CI tests for every payload shape you feed to Monty
- Avoid passing recursive or self-referential object graphs directly
When it happens
Trigger: Calling session.feedRun with an `inputs` object, returning a value from an externalLookup/external function callback, or wrapping an object in ClassInstance(...) whose attribute tree nests more than 48 levels (e.g. self-referential-looking deep linked lists, generated nested JSON).
Common situations: Passing deeply recursive data structures (ASTs, linked lists built from nested objects), accidental self-referencing objects serialized by a custom wrapper, or frameworks that produce very deeply nested config trees.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- ClassInstance marker instanceId must be a uuid string
- ${field} must be 'all', undefined or a list/Set of names, go
- memoryUsageLimit must be a non-negative safe integer
- invalid printFlushInterval: expected a non-negative number o
- Monty${typeName} timezoneName requires offsetSeconds
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/197944b7700fae52.
Report an issue: GitHub.