From 93324d1ec5d284d47099fc4ace9931c5814b92c5 Mon Sep 17 00:00:00 2001 From: Deepanshu Pal <40927968+DeepanshuPal@users.noreply.github.com> Date: Fri, 2 Oct 2026 21:24:49 +0530 Subject: [PATCH 1/4] fix(anthropic): reject requests whose content blocks are all unrecognized --- .../pass_through/adapters/transformation.py | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/litellm/llms/anthropic/pass_through/adapters/transformation.py b/litellm/llms/anthropic/pass_through/adapters/transformation.py index 022bc6337b5..4ad30bdcb92 100644 --- a/litellm/llms/anthropic/pass_through/adapters/transformation.py +++ b/litellm/llms/anthropic/pass_through/adapters/transformation.py @@ -636,6 +636,25 @@ class LiteLLMAnthropicMessagesAdapter: assistant_message["reasoning_content"] = reasoning_content new_messages.append(assistant_message) + has_conversational_input = any(m.get("role") != "system" for m in replayable_messages) + has_translated_conversation = any(m.get("role") != "system" for m in new_messages) + if has_conversational_input and not has_translated_conversation: + unrecognized_block_types = sorted( + { + str(content.get("type")) + for message in replayable_messages + if message.get("role") != "system" and isinstance(message.get("content"), list) + for content in message["content"] + if isinstance(content, dict) and content.get("type") is not None + } + ) + type_list = ", ".join(json.dumps(block_type) for block_type in unrecognized_block_types) or "unknown" + raise litellm.BadRequestError( + message=f"Anthropic pass-through: every message's content blocks were of unrecognized types ({type_list}), so translation produced an empty conversation. Refusing to dispatch a request with no messages.", + model=model or "", + llm_provider="anthropic", + ) + return new_messages @staticmethod @@ -1763,4 +1782,4 @@ class LiteLLMAnthropicMessagesAdapter: type="content_block_delta", index=current_content_block_index, delta=content_block_delta, - ) + ) From c2e23e8fff47c79d1220395b93072ee38afd59d7 Mon Sep 17 00:00:00 2001 From: Deepanshu Pal <40927968+DeepanshuPal@users.noreply.github.com> Date: Fri, 2 Oct 2026 21:26:59 +0530 Subject: [PATCH 2/4] test(anthropic): cover all-unrecognized content blocks --- ...al_pass_through_adapters_transformation.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/unit/llms/anthropic/pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py b/tests/unit/llms/anthropic/pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py index 06aaa4e61fb..c82f95ab0c0 100644 --- a/tests/unit/llms/anthropic/pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py +++ b/tests/unit/llms/anthropic/pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py @@ -5167,3 +5167,48 @@ def test_eager_input_streaming_tool_reaches_bedrock_converse_as_beta(): assert data["additionalModelRequestFields"]["anthropic_beta"] == ["fine-grained-tool-streaming-2025-05-14"] assert data["toolConfig"]["tools"][0]["toolSpec"]["inputSchema"]["json"] == EAGER_INPUT_SCHEMA + +def test_translate_anthropic_to_openai_rejects_all_unrecognized_content_blocks(): + request = { + "model": "openai/gpt-4o", + "max_tokens": 1024, + "messages": [{"role": "user", "content": [{"type": "not_a_real_block", "text": "hello"}]}], + } + + with pytest.raises(litellm.BadRequestError, match="not_a_real_block"): + LiteLLMAnthropicMessagesAdapter().translate_anthropic_to_openai(request) + + control_chars = { + **request, + "messages": [{"role": "user", "content": [{"type": "forged\nERROR: injected"}]}], + } + with pytest.raises(litellm.BadRequestError) as exc_info: + LiteLLMAnthropicMessagesAdapter().translate_anthropic_to_openai(control_chars) + assert "forged\\nERROR: injected" in str(exc_info.value) + assert "forged\nERROR: injected" not in str(exc_info.value) + + with_system = { + **request, + "messages": [ + {"role": "system", "content": "keep this"}, + *request["messages"], + ], + } + with pytest.raises(litellm.BadRequestError): + LiteLLMAnthropicMessagesAdapter().translate_anthropic_to_openai(with_system) + + mixed = { + "model": "openai/gpt-4o", + "max_tokens": 1024, + "messages": [ + { + "role": "user", + "content": [ + {"type": "not_a_real_block", "text": "ignored"}, + {"type": "text", "text": "hello"}, + ], + } + ], + } + openai_request, _ = LiteLLMAnthropicMessagesAdapter().translate_anthropic_to_openai(mixed) + assert openai_request["messages"] == [{"role": "user", "content": [{"type": "text", "text": "hello"}]}] From e8def90384594abdb8a51c383aa617c5a737948d Mon Sep 17 00:00:00 2001 From: Deepanshu Pal <40927968+DeepanshuPal@users.noreply.github.com> Date: Fri, 2 Oct 2026 21:27:37 +0530 Subject: [PATCH 3/4] style(anthropic): add blank line before new test --- ...nthropic_experimental_pass_through_adapters_transformation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/llms/anthropic/pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py b/tests/unit/llms/anthropic/pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py index c82f95ab0c0..2ded8e9aa90 100644 --- a/tests/unit/llms/anthropic/pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py +++ b/tests/unit/llms/anthropic/pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py @@ -5168,6 +5168,7 @@ def test_eager_input_streaming_tool_reaches_bedrock_converse_as_beta(): assert data["additionalModelRequestFields"]["anthropic_beta"] == ["fine-grained-tool-streaming-2025-05-14"] assert data["toolConfig"]["tools"][0]["toolSpec"]["inputSchema"]["json"] == EAGER_INPUT_SCHEMA + def test_translate_anthropic_to_openai_rejects_all_unrecognized_content_blocks(): request = { "model": "openai/gpt-4o", From 6d8059260752db3335fdcc1ac63b33b3e7229449 Mon Sep 17 00:00:00 2001 From: Deepanshu Pal <40927968+DeepanshuPal@users.noreply.github.com> Date: Fri, 2 Oct 2026 21:28:04 +0530 Subject: [PATCH 4/4] style(anthropic): fix indentation of final line --- litellm/llms/anthropic/pass_through/adapters/transformation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/llms/anthropic/pass_through/adapters/transformation.py b/litellm/llms/anthropic/pass_through/adapters/transformation.py index 4ad30bdcb92..7ff83954f5c 100644 --- a/litellm/llms/anthropic/pass_through/adapters/transformation.py +++ b/litellm/llms/anthropic/pass_through/adapters/transformation.py @@ -1782,4 +1782,4 @@ class LiteLLMAnthropicMessagesAdapter: type="content_block_delta", index=current_content_block_index, delta=content_block_delta, - ) + )