From d5cca6b97d8ba658705cf3bf1db1e52173cc8861 Mon Sep 17 00:00:00 2001 From: Tai An Date: Wed, 3 Jun 2026 09:12:26 -0700 Subject: [PATCH] fix(guardrails): scan Anthropic tool_result content blocks The Generic Guardrail API's _extract_input_text_and_images only read the "text" key from list content blocks, so Anthropic tool_result blocks (whose text lives under "content") were silently skipped. Tool outputs such as file reads and API responses bypassed all guardrail scanning, a PII/secret-leak gap in agentic workflows. Also handles the list form of tool_result content (blocks of type text). --- .../chat/guardrail_translation/handler.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/litellm/llms/anthropic/chat/guardrail_translation/handler.py b/litellm/llms/anthropic/chat/guardrail_translation/handler.py index 74dadee5ecb..4aea5c8178f 100644 --- a/litellm/llms/anthropic/chat/guardrail_translation/handler.py +++ b/litellm/llms/anthropic/chat/guardrail_translation/handler.py @@ -237,6 +237,24 @@ class AnthropicMessagesHandler(BaseTranslation): texts_to_check.append(text_str) task_mappings.append((msg_idx, int(content_idx))) + # Anthropic tool_result blocks carry their text under "content" + # (string or list of blocks), not "text". Without this, tool + # outputs (file reads, API responses) bypass guardrail scanning. + if content_item.get("type") == "tool_result": + tool_result_content = content_item.get("content") + if isinstance(tool_result_content, str): + texts_to_check.append(tool_result_content) + task_mappings.append((msg_idx, int(content_idx))) + elif isinstance(tool_result_content, list): + for block in tool_result_content: + if isinstance(block, dict): + block_text = block.get("text") + if block_text is not None: + texts_to_check.append(block_text) + task_mappings.append( + (msg_idx, int(content_idx)) + ) + # Extract images if content_item.get("type") == "image": source = content_item.get("source", {})