{"record":{"id":"16b4e561c4afa1e7","repo":"BerriAI/litellm","slug":"content-blocked-context-label-arguments-exceed","errorCode":null,"errorMessage":"Content blocked: {context_label} arguments exceed the maximum nesting depth","messagePattern":"Content blocked: (.+?) arguments exceed the maximum nesting depth","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py","lineNumber":1777,"sourceCode":"        if self._filter_single_text(text, detections=detections) != text:\n            raise HTTPException(\n                status_code=400,\n                detail={\n                    \"error\": (\n                        f\"Content blocked: {context_label} argument matched a masking rule on a non-rewritable field\"\n                    )\n                },\n            )\n\n    def _filter_argument_value(\n        self,\n        value: object,\n        detections: list[ContentFilterDetection],\n        context_label: str,\n        depth: int = 0,\n    ) -> object:\n        if depth > DEFAULT_MAX_RECURSE_DEPTH:\n            raise HTTPException(\n                status_code=400,\n                detail={\"error\": f\"Content blocked: {context_label} arguments exceed the maximum nesting depth\"},\n            )\n        if isinstance(value, str):\n            return self._filter_single_text(value, detections=detections)\n        if isinstance(value, (int, float)) and not isinstance(value, bool):\n            self._assert_argument_label_clean(str(value), detections, context_label)\n            return value\n        if isinstance(value, dict):\n            for key in value:\n                if isinstance(key, str):\n                    self._assert_argument_label_clean(key, detections, context_label)\n            return {\n                key: self._filter_argument_value(item, detections, context_label, depth + 1)\n                for key, item in value.items()\n            }\n        if isinstance(value, list):\n            return [self._filter_argument_value(item, detections, context_label, depth + 1) for item in value]","sourceCodeStart":1759,"sourceCodeEnd":1795,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py#L1759-L1795","documentation":"The content filter masks MCP tool-call arguments via recursive descent in _filter_argument_value, bounded by DEFAULT_MAX_RECURSE_DEPTH (default 100, overridable with the DEFAULT_MAX_RECURSE_DEPTH env var). Arguments nested deeper than the limit are rejected with HTTP 400 rather than scanned, so recursion cost stays bounded.","triggerScenarios":"An MCP tool-call arguments payload with more than 100 levels of nested dicts/lists - typically a serialization bug (self-referencing structure), a client loop that adds wrappers each iteration, or a deliberately pathological payload.","commonSituations":"Cyclic object graphs accidentally serialized into arguments; wrapper-in-a-loop bugs that add one nesting level per retry; machine-generated JSON from recursive data structures; payloads crafted to probe the gateway's limits.","solutions":["Flatten or simplify the tool arguments payload so nesting stays well under the limit (default 100)","Fix the client bug generating runaway nesting - look for accidental self-reference or wrappers added in a loop","If genuinely deeper payloads are required, raise the DEFAULT_MAX_RECURSE_DEPTH environment variable on the proxy process and restart it"],"exampleFix":"# before - loop adds a wrapper dict per attempt: depth grows until 400\nargs = payload\nfor attempt in retries:\n    args = {\"wrapper\": args}\n\n# after - send the payload once, keep it flat\nargs = payload","handlingStrategy":"validation","validationCode":"MAX_DEPTH = 100  \n  \ndef max_nesting_depth(value, depth=0) -> int:  \n    if isinstance(value, dict):  \n        return max((max_nesting_depth(v, depth + 1) for v in value.values()), default=depth)  \n    if isinstance(value, (list, tuple)):  \n        return max((max_nesting_depth(v, depth + 1) for v in value), default=depth)  \n    return depth  \n  \nif max_nesting_depth(tool_arguments) > MAX_DEPTH:  \n    raise ValueError(\"flatten arguments before sending: nesting exceeds guardrail limit\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Serialize tool arguments once, never re-wrap payloads in loops or retries","Break reference cycles (json.dumps with default=...) before building arguments","Set an explicit depth budget in your client and fail fast locally instead of round-tripping to the proxy"],"tags":["mcp","content-filter","nesting-depth","guardrail","http-400"],"backgroundTag":"json-nesting-depth-exceeded","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}