ComposioHQ/composio · error · ValueError

JSON Schema exceeds maximum nesting depth of {MAX_NODE_DEPTH

Error message

JSON Schema exceeds maximum nesting depth of {MAX_NODE_DEPTH}

What it means

to_strict_json_schema's _dedupe_required pass enforces a hard recursion cap (MAX_NODE_DEPTH) on schema node depth. Exceeding it raises ValueError, protecting against pathological or cyclic-ish schemas that would otherwise recurse unboundedly during strict-schema conversion.

Source

Thrown at python/composio/utils/strict_schema.py:189

    node_type = node.get("type")
    if isinstance(node_type, str):
        return node_type == "null"
    if isinstance(node_type, list):
        return "null" in node_type
    if isinstance(node.get("enum"), list):
        return None in node["enum"]
    if "const" in node:
        return node["const"] is None
    for keyword in ("anyOf", "oneOf"):
        branches = node.get(keyword)
        if isinstance(branches, list):
            return any(_schema_accepts_null(b, root) for b in branches)
    return True


def _dedupe_required(value: t.Any, is_schema: bool = True, depth: int = 0) -> t.Any:
    if depth > MAX_NODE_DEPTH:
        raise ValueError(
            f"JSON Schema exceeds maximum nesting depth of {MAX_NODE_DEPTH}"
        )
    if isinstance(value, list):
        return [_dedupe_required(item, is_schema, depth + 1) for item in value]
    if not isinstance(value, dict):
        return value
    clone: dict[str, t.Any] = {}
    for key, child in value.items():
        if is_schema and key == "required" and isinstance(child, list):
            clone[key] = list(dict.fromkeys(child))
        else:
            clone[key] = _dedupe_required(
                child, is_schema and key not in INSTANCE_VALUE_KEYWORDS, depth + 1
            )
    return clone


class _Walker:

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Reduce real nesting by using $ref/$defs instead of fully inlined recursive structures
  2. Check for accidental duplication: a transformation pass that re-wraps the schema each run can multiply depth — diff depth before/after each step
  3. If legitimately deep, split the schema or raise the limit only if you control the constant and accept the risk
  4. Sanitize/truncate untrusted schemas before strict conversion

Example fix

# before (recursive inline)
{"type": "object", "properties": {"child": {"type": "object", "properties": {"child": {...deep...}}}}}
# after
{"$defs": {"node": {"type": "object", "properties": {"child": {"$ref": "#/$defs/node"}}}}, "$ref": "#/$defs/node"}
Defensive patterns

Strategy: validation

Validate before calling

def schema_depth(v, d=0):
    if d > 100: return d
    if isinstance(v, dict): return max([schema_depth(x, d+1) for x in v.values()], default=d)
    if isinstance(v, list): return max([schema_depth(x, d+1) for x in v], default=d)
    return d
assert schema_depth(schema) < MAX_NODE_DEPTH

Try / catch

try:
    strict = to_strict_json_schema(schema)
except ValueError as e:
    if "nesting depth" in str(e):
        schema = flatten_with_defs(schema)
        strict = to_strict_json_schema(schema)

Prevention

When it happens

Trigger: Feeding a schema whose nested allOf/properties/items chains exceed the depth limit — deeply recursive definitions, schemas where $ref inlining produced self-nesting, or adversarially deep generated specs.

Common situations: Recursive schemas (trees, linked structures) inlined without $refs; a bug duplicating nested levels on each transformation pass; schemas from untrusted/auto-generated sources; accidental self-referential definitions after refactoring.

Related errors


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