ComposioHQ/composio · error · JSONSchemaRefResolutionError

JSON Schema node depth exceeded cap ({MAX_NODE_DEPTH})

Error message

JSON Schema node depth exceeded cap ({MAX_NODE_DEPTH})

What it means

dereference_json_schema refuses to walk schemas whose object nesting exceeds MAX_NODE_DEPTH (512). This cap exists so pathological schemas (cyclic data or billion-laughs-style expansion) raise a clean error instead of exhausting Python's recursion limit.

Source

Thrown at python/composio/utils/json_schema.py:170

    root = t.cast(t.Dict[str, t.Any], schema)
    # Identity-based cycle guard for the *current* walk path. Python doesn't
    # suffer JS-style prototype pollution, so unlike the TS port there's no
    # need to filter ``__proto__``/``constructor`` keys while cloning.
    visiting: t.Set[int] = set()

    # ``walk`` uses explicit loops (not comprehensions) so each level of schema
    # nesting costs exactly one Python stack frame. That keeps ``MAX_NODE_DEPTH``
    # the binding limit under the interpreter's default recursion ceiling, so a
    # pathologically deep schema fails with our typed error rather than a bare
    # ``RecursionError`` (which is also caught at the call site as a backstop).
    def walk(
        node: t.Any,
        visited_refs: t.FrozenSet[str],
        chain_depth: int,
        node_depth: int,
    ) -> t.Any:
        if node_depth >= MAX_NODE_DEPTH:
            raise JSONSchemaRefResolutionError(
                f"JSON Schema node depth exceeded cap ({MAX_NODE_DEPTH})"
            )

        if isinstance(node, list):
            node_id = id(node)
            if node_id in visiting:
                return _cycle_break_sentinel()
            visiting.add(node_id)
            try:
                cloned_list: t.List[t.Any] = []
                for item in node:
                    cloned_list.append(
                        walk(item, visited_refs, chain_depth, node_depth + 1)
                    )
                return cloned_list
            finally:
                visiting.discard(node_id)

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Inspect the schema's actual depth and collapse unnecessary nesting
  2. Convert deeply repeated inline structures into $defs + $ref so the walker dedupes
  3. Truncate or reject the input schema before calling the SDK if it comes from an untrusted source
  4. Report it if a legitimate Composio tool schema trips the cap
Defensive patterns

Strategy: validation

Validate before calling

def schema_depth(n):
    if isinstance(n, dict): return 1 + max((schema_depth(v) for v in n.values()), default=0)
    if isinstance(n, list): return 1 + max((schema_depth(v) for v in n), default=0)
    return 0
assert schema_depth(schema) < 512

Try / catch

from composio.exceptions import JSONSchemaRefResolutionError
try:
    out = dereference_json_schema(schema)
except JSONSchemaRefResolutionError:
    schema = simplify(schema)  # collapse inline duplication into $defs/$ref
    out = dereference_json_schema(schema)

Prevention

When it happens

Trigger: Passing a schema with more than 512 levels of nested properties/items/anyOf containers, or a self-referential structure that expands without $ref cycle protection, to dereference_json_schema or any wrapper (substitute_file_uploads, alias_tool_input_schema).

Common situations: Machine-generated schemas from recursive models (tree types without $ref indirection); malicious or corrupted schema payloads; accidental self-nesting when a schema is built programmatically.

Related errors


AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28). Data as JSON: /api/errors/3a4089dcd019e2f4. Report an issue: GitHub.