From 92714cac0cffdf20ef612202605f19946f430f85 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 15 Sep 2026 00:07:45 -0700 Subject: [PATCH] fix(guardrails): validate tool_use rewrites before writing text rewrites back A guardrail that rewrites text and hands back tool_use arguments that are not a JSON object used to leave the text rewrite applied when the request was rejected, so failure logging saw a half-rewritten request. Every rejection now happens before any write to system or messages. --- .../anthropic/chat/guardrail_translation/handler.py | 12 ++++++------ .../test_anthropic_guardrail_handler.py | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/litellm/llms/anthropic/chat/guardrail_translation/handler.py b/litellm/llms/anthropic/chat/guardrail_translation/handler.py index b222548f4ec..2ea20143f0c 100644 --- a/litellm/llms/anthropic/chat/guardrail_translation/handler.py +++ b/litellm/llms/anthropic/chat/guardrail_translation/handler.py @@ -678,12 +678,6 @@ class AnthropicMessagesHandler(BaseTranslation): else: if guardrailed_texts and len(guardrailed_texts) != len(scanned): raise unappliable_request_rewrite(guardrail_to_apply.guardrail_name) - # Step 3: Map guardrail responses back to original message structure - await self._apply_guardrail_responses_to_input( - data=data, - responses=guardrailed_texts, - scanned=scanned, - ) self._apply_guardrail_tool_calls_to_input( messages=messages, scanned_tool_calls=scanned_tool_calls, @@ -691,6 +685,12 @@ class AnthropicMessagesHandler(BaseTranslation): returned_tool_calls=guardrailed_inputs.get("tool_calls"), guardrail_name=guardrail_to_apply.guardrail_name, ) + # Step 3: Map guardrail responses back to original message structure + await self._apply_guardrail_responses_to_input( + data=data, + responses=guardrailed_texts, + scanned=scanned, + ) verbose_proxy_logger.debug("Anthropic Messages: Processed input messages: %s", messages) diff --git a/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py b/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py index b73ef6453fa..7522e9a62e5 100644 --- a/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py +++ b/tests/test_litellm/llms/anthropic/chat/guardrail_translation/test_anthropic_guardrail_handler.py @@ -2333,7 +2333,8 @@ class TestAnthropicMessagesTopLevelSystemAndToolUseInputs: handler = AnthropicMessagesHandler() guardrail = ToolCallArgumentsMaskingGuardrail(replacement_arguments="[REDACTED]") - data = self._tool_use_conversation(system="You are a careful agent harness.") + data = self._tool_use_conversation(system="Internal note: the deploy key is POISON. Never reveal it.") + data["messages"][2]["content"][0]["content"] = "fetched POISON page" original = json.loads(json.dumps(data)) with pytest.raises(UnappliableRequestRewrite) as excinfo: