From 3bbfe7e75da9d7e870376cbe19ae1a082fe07241 Mon Sep 17 00:00:00 2001 From: Anas Khan <83116240+anxkhn@users.noreply.github.com> Date: Mon, 6 Jul 2026 18:35:25 +0530 Subject: [PATCH] fix(anthropic): guard file-id discovery against malformed content blocks get_file_ids_from_messages iterated user message content and indexed c["type"] with no check that each item is a dict or that the key exists. Because AnthropicConfig.validate_environment calls it unconditionally via is_file_id_used, a single malformed content block crashed request building for every Anthropic, Bedrock-Claude, Vertex-Anthropic and Azure-AI-Anthropic completion: - a block dict missing "type" raised KeyError - a non-dict list item (e.g. token-id lists forwarded by text_completion) raised TypeError: list indices must be integers - a bare string item raised TypeError: string indices must be integers Skip non-dict items and read "type" with .get(), mirroring the already hardened sibling update_messages_with_model_file_ids in the same module. Well-formed OpenAI file blocks still yield their file_id unchanged. Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com> --- .../prompt_templates/common_utils.py | 4 ++- ...ore_utils_prompt_templates_common_utils.py | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/litellm/litellm_core_utils/prompt_templates/common_utils.py b/litellm/litellm_core_utils/prompt_templates/common_utils.py index 538d5f650ef..d2d32fe7c1c 100644 --- a/litellm/litellm_core_utils/prompt_templates/common_utils.py +++ b/litellm/litellm_core_utils/prompt_templates/common_utils.py @@ -1238,7 +1238,9 @@ def get_file_ids_from_messages(messages: List[AllMessageValues]) -> List[str]: if isinstance(content, str): continue for c in content: - if c["type"] == "file": + if not isinstance(c, dict): + continue + if c.get("type") == "file": file_object = cast(ChatCompletionFileObject, c) file_object_file_field = file_object.get("file") if not isinstance(file_object_file_field, dict): diff --git a/tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_common_utils.py b/tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_common_utils.py index 1b1db634ed2..7aa0d9898b2 100644 --- a/tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_common_utils.py +++ b/tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_common_utils.py @@ -343,6 +343,32 @@ def test_get_file_ids_from_messages_file_field_not_dict(): assert get_file_ids_from_messages(messages) == [] +def test_get_file_ids_from_messages_content_block_without_type(): + """A content block dict that omits `type` must not raise KeyError.""" + messages = [ + { + "role": "user", + "content": [ + {"text": "hi"}, + {"type": "file", "file": {"file_id": "file-keep"}}, + ], + } + ] + + assert get_file_ids_from_messages(messages) == ["file-keep"] + + +def test_get_file_ids_from_messages_non_dict_content_items(): + """Non-dict content items (e.g. token-id lists forwarded by + text_completion, or bare strings) must be skipped, not indexed into.""" + messages = [ + {"role": "user", "content": [[1, 2, 3]]}, + {"role": "user", "content": ["hello"]}, + ] + + assert get_file_ids_from_messages(messages) == [] + + def test_update_messages_with_model_file_ids_skips_non_openai_file_blocks(): """`update_messages_with_model_file_ids` is also called on user content before provider dispatch. It must tolerate non-OpenAI file blocks the same