From 4f6fad98a666d4384252e4ad15ed9edab1df1c33 Mon Sep 17 00:00:00 2001 From: S0ngRu1 <1922909737@qq.com> Date: Tue, 24 Mar 2026 18:14:38 +0800 Subject: [PATCH 1/2] fix(vertex_ai/gemini): raise BadRequestError when image_url or url field is missing Previously, direct key access on img_element["image_url"] and raw_image_url["url"] raised bare KeyErrors, resulting in 500 APIConnectionErrors. Use .get() for both fields and raise a descriptive BadRequestError (400) immediately when either is absent. Also apply Black formatting to audit_logs.py to pass lint check. Tests: tests/test_litellm/llms/vertex_ai/gemini/test_gemini_image_url_missing_field.py --- .../llms/vertex_ai/gemini/transformation.py | 23 ++++++++++---- .../test_gemini_image_url_missing_field.py | 30 +++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 tests/test_litellm/llms/vertex_ai/gemini/test_gemini_image_url_missing_field.py diff --git a/litellm/llms/vertex_ai/gemini/transformation.py b/litellm/llms/vertex_ai/gemini/transformation.py index 9afa5dec4658..fa20386da79e 100644 --- a/litellm/llms/vertex_ai/gemini/transformation.py +++ b/litellm/llms/vertex_ai/gemini/transformation.py @@ -351,15 +351,28 @@ def _gemini_convert_messages_with_history( # noqa: PLR0915 img_element = element format: Optional[str] = None media_resolution_enum: Optional[Dict[str, str]] = None - if isinstance(img_element["image_url"], dict): - image_url = img_element["image_url"]["url"] - format = img_element["image_url"].get("format") - detail = img_element["image_url"].get("detail") + raw_image_url = img_element.get("image_url") + if raw_image_url is None: + raise litellm.BadRequestError( + message="Invalid message content: element type is 'image_url' but 'image_url' field is missing ", + model=model, + llm_provider="vertex_ai", + ) + if isinstance(raw_image_url, dict): + image_url = raw_image_url.get("url") + if image_url is None: + raise litellm.BadRequestError( + message="Invalid message content: element type is 'image_url' but 'url' field is missing inside 'image_url' ", + model=model, + llm_provider="vertex_ai", + ) + format = raw_image_url.get("format") + detail = raw_image_url.get("detail") media_resolution_enum = ( _convert_detail_to_media_resolution_enum(detail) ) else: - image_url = img_element["image_url"] + image_url = raw_image_url _part = _process_gemini_media( image_url=image_url, format=format, diff --git a/tests/test_litellm/llms/vertex_ai/gemini/test_gemini_image_url_missing_field.py b/tests/test_litellm/llms/vertex_ai/gemini/test_gemini_image_url_missing_field.py new file mode 100644 index 000000000000..0113fa0fc0eb --- /dev/null +++ b/tests/test_litellm/llms/vertex_ai/gemini/test_gemini_image_url_missing_field.py @@ -0,0 +1,30 @@ +import pytest +from typing import List, cast + +import litellm +from litellm.llms.vertex_ai.gemini.transformation import ( + _gemini_convert_messages_with_history, +) +from litellm.types.llms.openai import AllMessageValues + + +def test_missing_image_url_field_raises_bad_request_error(): + """When element type is 'image_url' but 'image_url' field is missing, a BadRequestError is raised.""" + messages = cast( + List[AllMessageValues], + [{"role": "user", "content": [{"type": "image_url"}]}], + ) + with pytest.raises(litellm.BadRequestError) as exc_info: + _gemini_convert_messages_with_history(messages, model="gemini-1.5-pro") + assert "'image_url' field is missing" in str(exc_info.value) + + +def test_missing_url_inside_image_url_dict_raises_bad_request_error(): + """When image_url is a dict but 'url' key is absent, a BadRequestError is raised.""" + messages = cast( + List[AllMessageValues], + [{"role": "user", "content": [{"type": "image_url", "image_url": {"detail": "high"}}]}], + ) + with pytest.raises(litellm.BadRequestError) as exc_info: + _gemini_convert_messages_with_history(messages, model="gemini-1.5-pro") + assert "'url' field is missing inside" in str(exc_info.value) From 79f2e327eb5160be8edba6597d5b73a750a48826 Mon Sep 17 00:00:00 2001 From: S0ngRu1 <1922909737@qq.com> Date: Wed, 13 May 2026 14:26:56 +0800 Subject: [PATCH 2/2] test(vertex_ai/gemini): add tests for BadRequestError on null and empty image_url Added new test cases to ensure that a BadRequestError is raised when the image_url key is explicitly set to null or is an empty dictionary. These tests validate the handling of edge cases for the image_url field in the Gemini model. --- .../test_gemini_image_url_missing_field.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_litellm/llms/vertex_ai/gemini/test_gemini_image_url_missing_field.py b/tests/test_litellm/llms/vertex_ai/gemini/test_gemini_image_url_missing_field.py index 0113fa0fc0eb..10fc68ecaad6 100644 --- a/tests/test_litellm/llms/vertex_ai/gemini/test_gemini_image_url_missing_field.py +++ b/tests/test_litellm/llms/vertex_ai/gemini/test_gemini_image_url_missing_field.py @@ -28,3 +28,25 @@ def test_missing_url_inside_image_url_dict_raises_bad_request_error(): with pytest.raises(litellm.BadRequestError) as exc_info: _gemini_convert_messages_with_history(messages, model="gemini-1.5-pro") assert "'url' field is missing inside" in str(exc_info.value) + + +def test_explicit_null_image_url_raises_bad_request_error(): + """When image_url key is present but explicitly null, a BadRequestError is raised.""" + messages = cast( + List[AllMessageValues], + [{"role": "user", "content": [{"type": "image_url", "image_url": None}]}], + ) + with pytest.raises(litellm.BadRequestError) as exc_info: + _gemini_convert_messages_with_history(messages, model="gemini-1.5-pro") + assert "'image_url' field is missing" in str(exc_info.value) + + +def test_empty_dict_image_url_raises_bad_request_error(): + """When image_url is an empty dict (no url), a BadRequestError is raised.""" + messages = cast( + List[AllMessageValues], + [{"role": "user", "content": [{"type": "image_url", "image_url": {}}]}], + ) + with pytest.raises(litellm.BadRequestError) as exc_info: + _gemini_convert_messages_with_history(messages, model="gemini-1.5-pro") + assert "'url' field is missing inside" in str(exc_info.value)