From d0bb701c34acb201a7012e0de3a89b89fc079860 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 30 Oct 2026 15:24:04 -0700 Subject: [PATCH] fix(anthropic): point the text attachment decode 400 at a file block's format A downloaded text file reaches the decoder without its server's charset since main strips it, so the old hint to name the charset in the data URI could not help a URL caller. A file block's format takes text/plain;charset=... for both URLs and data URIs --- .../prompt_templates/factory.py | 3 ++- ...llm_core_utils_prompt_templates_factory.py | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py index 994ed905a94..8c4afddc944 100644 --- a/litellm/litellm_core_utils/prompt_templates/factory.py +++ b/litellm/litellm_core_utils/prompt_templates/factory.py @@ -1856,7 +1856,8 @@ def _anthropic_text_document_source( except (LookupError, UnicodeDecodeError) as decode_error: raise litellm.BadRequestError( message=f"Text attachment could not be read as {charset!r}: {decode_error}. " - "Send it as utf-8, or name its encoding in the data URI (data:text/plain;charset=...).", + "Send it as utf-8, or name its encoding (text/plain;charset=...) in the data URI " + "or in a file block's format.", model=None, llm_provider="anthropic", ) from decode_error diff --git a/tests/unit/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py b/tests/unit/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py index db23b60da28..ef45fd03f19 100644 --- a/tests/unit/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py +++ b/tests/unit/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py @@ -9389,6 +9389,25 @@ def test_anthropic_messages_pt_rejects_a_text_attachment_it_cannot_decode(): assert "utf-8" in str(raised.value) assert undeclared_latin1.split(",", 1)[1] not in str(raised.value) + assert "file block's format" in str(raised.value) + + +def test_anthropic_messages_pt_reads_a_text_file_block_in_the_charset_its_format_names(): + undeclared_latin1: Final = "data:text/plain;base64," + base64.b64encode("caf\xe9".encode("iso-8859-1")).decode() + messages: Final = [ + { + "role": "user", + "content": [ + {"type": "file", "file": {"file_data": undeclared_latin1, "format": "text/plain;charset=iso-8859-1"}} + ], + } + ] + + result: Final = anthropic_messages_pt(messages=messages, model="claude-sonnet-5", llm_provider="anthropic") + + assert result[0]["content"] == [ + {"type": "document", "source": {"type": "text", "media_type": "text/plain", "data": "café"}} + ] @pytest.mark.parametrize("native_input", [{"city": "lyon"}, "[1, 2]"])