ComposioHQ/composio · error · ValueError

Tool arguments exceed maximum nesting depth of {MAX_NODE_DEP

Error message

Tool arguments exceed maximum nesting depth of {MAX_NODE_DEPTH}

What it means

omit_null_tool_arguments walks the argument payload against the schema to drop null-valued optional fields, and _omit_nulls enforces MAX_NODE_DEPTH on the combined value/schema recursion. Deeply nested arguments (or a schema that makes the walker chase many branches) exceed the cap and raise ValueError.

Source

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

            return candidate
        for keyword in ("anyOf", "oneOf"):
            branches = candidate.get(keyword)
            if not isinstance(branches, list):
                continue
            for branch in branches:
                found = find(branch, depth + 1)
                if found is not None:
                    return found
        return None

    return find(resolved, 0) or resolved


def _omit_nulls(
    value: t.Any, schema: t.Any, root: dict[str, t.Any], depth: int
) -> t.Any:
    if depth > MAX_NODE_DEPTH:
        raise ValueError(
            f"Tool arguments exceed maximum nesting depth of {MAX_NODE_DEPTH}"
        )
    node = _select_branch_for(schema, value, root) or {}
    if isinstance(value, list):
        items = node.get("items") if isinstance(node.get("items"), dict) else None
        return [_omit_nulls(item, items, root, depth + 1) for item in value]
    if not isinstance(value, dict):
        return value
    declared = node.get("properties")
    properties: dict[str, t.Any] = declared if isinstance(declared, dict) else {}
    clone: dict[str, t.Any] = {}
    for key, child in value.items():
        property_schema = properties.get(key)
        if child is None:
            if property_schema is None or _schema_accepts_null(property_schema, root):
                clone[key] = child
            continue
        clone[key] = _omit_nulls(child, property_schema, root, depth + 1)

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Flatten arguments before the call: send big/deep blobs as a single string field or file reference rather than structured arguments
  2. Pre-check depth with a quick recursive counter and split/truncate the payload
  3. Simplify the tool's input schema so anyOf/branch selection doesn't compound depth
  4. If the payload is legitimately huge, use file upload paths instead of inline arguments

Example fix

# before
args = json.loads(deep_document_text)  # possibly 50+ levels
tool.run(args)
# after
args = {"document": deep_document_text}
tool.run(args)
Defensive patterns

Strategy: validation

Validate before calling

def payload_depth(v, d=0):
    if isinstance(v, dict): return max([payload_depth(x, d+1) for x in v.values()], default=d)
    if isinstance(v, list): return max([payload_depth(x, d+1) for x in v], default=d)
    return d
if payload_depth(args) > 50: args = {"blob": json.dumps(args)}

Try / catch

try:
    tool.run(args)
except ValueError as e:
    if "nesting depth" in str(e):
        tool.run({"document": json.dumps(args)})

Prevention

When it happens

Trigger: Calling a tool (or explicitly omit_null_tool_arguments) with arguments nested deeper than the limit — e.g. deeply nested JSON payloads from file parsing, or list-of-list structures combined with anyOf-heavy schemas that multiply traversal depth.

Common situations: Uploading parsed JSON documents (ASTs, configs, serialized trees) as tool arguments; LLMs generating extremely nested output; payloads that grew from data-driven nesting without a flattening step.

Related errors


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