From 749b7ab141419cb2169a54f476a802f37420c96d Mon Sep 17 00:00:00 2001 From: Igal Boxerman Date: Sun, 22 Mar 2026 10:15:34 +0200 Subject: [PATCH] fix(guardrails): return HTTP 400 instead of 500 for guardrail-blocked requests GuardrailRaisedException had no status_code attribute, so the generic exception handler in _handle_llm_api_exception() defaulted to 500. Guardrail blocks are client errors (the request content violated policy), not server errors. Add status_code=400 to GuardrailRaisedException so the proxy returns the correct HTTP status. --- litellm/exceptions.py | 2 ++ .../guardrails/guardrail_hooks/test_generic_guardrail_api.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/litellm/exceptions.py b/litellm/exceptions.py index abdba09dd8d..f62848295a0 100644 --- a/litellm/exceptions.py +++ b/litellm/exceptions.py @@ -917,10 +917,12 @@ class GuardrailRaisedException(Exception): guardrail_name: Optional[str] = None, message: str = "", should_wrap_with_default_message: bool = True, + status_code: int = 400, ): default_message = f"Guardrail raised an exception, Guardrail: {guardrail_name}, Message: {message}" self.guardrail_name = guardrail_name self.message = default_message if should_wrap_with_default_message else message + self.status_code = status_code super().__init__(self.message) diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_generic_guardrail_api.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_generic_guardrail_api.py index e01038cd35f..7c1a94609d5 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_generic_guardrail_api.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_generic_guardrail_api.py @@ -553,6 +553,8 @@ class TestGuardrailActions: # Verify the exception has the clean error message (no wrapper) assert str(exc_info.value) == "Content contains harmful instructions" assert exc_info.value.guardrail_name == "generic_guardrail_api" + # Guardrail blocks should map to 400, not 500 + assert exc_info.value.status_code == 400 @pytest.mark.asyncio async def test_action_intervened_modifies_content(