{"record":{"id":"f2f34ce4a131054e","repo":"ComposioHQ/composio","slug":"json-schema-ref-chain-exceeded-depth-cap-max-re","errorCode":null,"errorMessage":"JSON Schema $ref chain exceeded depth cap ({MAX_REF_CHAIN_DEPTH}): {ref}","messagePattern":"JSON Schema \\$ref chain exceeded depth cap \\((.+?)\\): (.+?)","errorType":"exception","errorClass":"JSONSchemaRefResolutionError","httpStatus":null,"severity":"error","filePath":"python/composio/utils/json_schema.py","lineNumber":211,"sourceCode":"        if node_id in visiting:\n            return _cycle_break_sentinel()\n        visiting.add(node_id)\n        try:\n            ref = node.get(\"$ref\")\n            ref = ref if isinstance(ref, str) else None\n\n            # External refs and non-``$ref`` nodes both pass through the same\n            # reflective clone path.\n            if ref is None or not ref.startswith(\"#\"):\n                if ref is not None:\n                    logger.warning(\"Leaving external $ref untouched: %s\", ref)\n                cloned: t.Dict[str, t.Any] = {}\n                for key, value in node.items():\n                    cloned[key] = walk(value, visited_refs, chain_depth, node_depth + 1)\n                return cloned\n\n            if chain_depth >= MAX_REF_CHAIN_DEPTH:\n                raise JSONSchemaRefResolutionError(\n                    f\"JSON Schema $ref chain exceeded depth cap \"\n                    f\"({MAX_REF_CHAIN_DEPTH}): {ref}\",\n                    meta={\"ref\": ref},\n                )\n            if ref in visited_refs:\n                return _cycle_break_sentinel()\n\n            resolution = _try_resolve_pointer(root, ref)\n            if resolution.ok:\n                target: t.Any = resolution.value\n            elif on_unresolved == \"sentinel\":\n                if on_replace is not None and resolution.reason is not None:\n                    on_replace(ref, resolution.reason)\n                target = {\n                    **_cycle_break_sentinel(),\n                    \"description\": UNRESOLVED_REF_DESCRIPTION,\n                }\n            else:","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/python/composio/utils/json_schema.py#L193-L229","documentation":"A chain of $ref → $ref → $ref longer than MAX_REF_CHAIN_DEPTH (100) was hit while inlining references. Like the node-depth cap, it guards against cyclic or billion-laughs-style reference bombs and raises JSONSchemaRefResolutionError instead of blowing the recursion limit.","triggerScenarios":"A schema where following $ref links (each hop increments chain_depth) exceeds 100 hops — typically a reference cycle that the visited_refs set didn't break (e.g. refs parameterized differently) or a deliberately chained bomb schema.","commonSituations":"Cyclic $defs (A refs B, B refs A) that slip past cycle detection; adversarial schemas; schemas generated from mutually recursive types.","solutions":["Check meta['ref'] in the error to find the offending reference and break the cycle","Rewrite cyclic schemas using bounded recursion or drop the cycle","Reject untrusted schemas before processing"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import json\ndef ref_chain_exceeds(schema, cap=100):\n    defs = schema.get('$defs', schema.get('definitions', {}))\n    def chain(name, seen):\n        if name in seen: return True  # cycle\n        target = defs.get(name)\n        if not isinstance(target, dict): return False\n        nxt = target.get('$ref')\n        if isinstance(nxt, str) and nxt.startswith('#/$defs/'):\n            return chain(nxt.split('/')[-1], seen | {name})\n        return False\n    return any(chain(k, set()) for k in defs)","typeGuard":null,"tryCatchPattern":"from composio.exceptions import JSONSchemaRefResolutionError\ntry:\n    out = dereference_json_schema(schema)\nexcept JSONSchemaRefResolutionError as e:\n    if 'ref' in getattr(e, 'meta', {}):\n        defs = schema.setdefault('$defs', {})\n        defs.pop(e.meta['ref'].split('/')[-1], None)\n    out = dereference_json_schema(schema, on_unresolved=\"sentinel\")","preventionTips":["Break $ref cycles when generating schemas from recursive types","Treat very long ref chains in third-party specs as a red flag"],"tags":["json-schema","ref-cycle","depth-limit","python"],"backgroundTag":"json-schema-ref-cycle","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}