{"record":{"id":"571aa0b4000af34a","repo":"BerriAI/litellm","slug":"guardrail-violation","errorCode":"guardrail_violation","errorMessage":"guardrail_violation","messagePattern":"guardrail_violation","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"litellm/proxy/_experimental/mcp_server/rest_endpoints.py","lineNumber":1067,"sourceCode":"                    \"server_name\": e.server_name,\n                    \"missing\": e.missing,\n                    \"setup_url\": e.setup_url,\n                },\n            )\n        except BlockedPiiEntityError as e:\n            verbose_logger.error(\"BlockedPiiEntityError in MCP tool call: %s\", e)\n            raise HTTPException(\n                status_code=400,\n                detail={\n                    \"error\": \"blocked_pii_entity\",\n                    \"message\": str(e),\n                    \"entity_type\": getattr(e, \"entity_type\", None),\n                    \"guardrail_name\": getattr(e, \"guardrail_name\", None),\n                },\n            )\n        except GuardrailRaisedException as e:\n            verbose_logger.error(\"GuardrailRaisedException in MCP tool call: %s\", e)\n            raise HTTPException(\n                status_code=400,\n                detail={\n                    \"error\": \"guardrail_violation\",\n                    \"message\": str(e),\n                    \"guardrail_name\": getattr(e, \"guardrail_name\", None),\n                },\n            )\n        except MCPUpstreamAuthError as e:\n            # A client-forwarded pass-through upstream 401 from either the direct or the virtual call\n            # branch. Relay it as a 401 + WWW-Authenticate so the MCP client can re-run upstream OAuth,\n            # and log at info: an expected caller-must-reauth signal, not an operator-actionable error.\n            verbose_logger.info(\"MCP tool call relaying upstream HTTP %s\", e.status_code)\n            raise _relay_upstream_auth_http_exception(e, request)\n        except HTTPException as e:\n            # Locally generated denials (tool/server permission, IP filtering, BYOK) stay at error level\n            # so restriction probing keeps full monitoring visibility; the relayed upstream 401 above is\n            # the only status demoted to info.\n            verbose_logger.error(\"HTTPException in MCP tool call: %s\", e)","sourceCodeStart":1049,"sourceCodeEnd":1085,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/_experimental/mcp_server/rest_endpoints.py#L1049-L1085","documentation":"GuardrailRaisedException is the generic guardrail rejection: an active guardrail (preset or custom) raised during the pre-call check of an MCP tool call. This handler converts it to HTTP 400 with error code guardrail_violation plus guardrail_name. It differs from blocked_pii_entity in that it covers any blocking guardrail decision, not only PII entity blocking.","triggerScenarios":"A guardrail in blocking mode (banned keywords/URLs/topics, prompt-injection or jailbreak detection) matches content in the tool call; guardrail hooks run inside the pre-call check and reject the request before it reaches the upstream MCP server.","commonSituations":"A security team enables strict guardrails on all traffic including MCP tools; agent prompts that quote untrusted web content trip keyword rules.","solutions":["Read guardrail_name from the response and check that guardrail's logs to see which rule matched.","Change the request content to satisfy the rule (remove banned terms/URLs/topics).","Ask the admin to scope the guardrail so it does not block legitimate MCP tool traffic, or to adjust the rule thresholds."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"except httpx.HTTPStatusError as e:\n    d = e.response.json().get(\"detail\", {})\n    if e.response.status_code == 400 and isinstance(d, dict) and d.get(\"error\") == \"guardrail_violation\":\n        # guardrail_name identifies the rule set; do not blind-retry — content must change\n        raise PolicyRejected(guardrail=d.get(\"guardrail_name\"), reason=str(d.get(\"message\"))) from e\n    raise","preventionTips":["Treat 400 guardrail_violation as permanent for that payload — change the content, do not retry unchanged.","Keep a mapping of active guardrails and their rules so tool payloads are pre-cleaned.","Log guardrail_name with every rejection to spot over-broad rules early."],"tags":["guardrails","mcp","content-filter","http-400"],"backgroundTag":"guardrail-content-blocked","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}