fix(gemini): handle Gemini Files API URIs without fetching (#24922)

* fix(gemini): handle Gemini Files API URIs without fetching

Fixes #24907

When a file is uploaded via the Gemini Files API, the returned URI
(https://generativelanguage.googleapis.com/v1beta/files/...) starts
with 'https://' and hits the generic HTTPS handler in
_process_gemini_media(). That handler calls
_get_image_mime_type_from_url() which tries to fetch the URL — but
Gemini Files API URLs return 403 when accessed directly, causing:
  'Unable to determine mime type for file_id: ...'

Fix: add an early elif that matches Gemini Files API URLs and passes
them through as file_data without trying to fetch the URL. When an
explicit format is provided it's included; otherwise the Gemini API
infers the MIME type from its stored metadata.

Exactly matches the fix direction suggested by the issue reporter
(rodriciru).

* fix: anchor Gemini Files API URL check with startswith

Address greptile P2: replace `in` substring check with `startswith`
to prevent query-string injection bypass (e.g.
`https://evil.com/?ref=https://generativelanguage...`).

Also adds trailing slash to match only valid file URIs.

---------

Co-authored-by: voidborne-d <voidborne-d@users.noreply.github.com>
This commit is contained in:
d 🔹 2026-04-02 11:13:15 +08:00 committed by Sameer Kankute
parent 7e58c7139a
commit 9d6983c4c0
No known key found for this signature in database
2 changed files with 62 additions and 0 deletions

View file

@ -212,6 +212,21 @@ def _process_gemini_media(
return _apply_gemini_metadata(
part, model, media_resolution_enum, video_metadata
)
elif image_url.startswith(
"https://generativelanguage.googleapis.com/v1beta/files/"
):
# Gemini Files API URIs — the file is already uploaded to Google's
# servers; pass the URI through as file_data without fetching it.
# These URLs return 403 when accessed directly, so we must not try
# to resolve their MIME type via HTTP.
if format:
file_data = FileDataType(mime_type=format, file_uri=image_url)
else:
file_data = FileDataType(file_uri=image_url)
part = {"file_data": file_data}
return _apply_gemini_3_metadata(
part, model, media_resolution_enum, video_metadata
)
elif (
"https://" in image_url
and (image_type := format or _get_image_mime_type_from_url(image_url))

View file

@ -1291,6 +1291,53 @@ def test_file_data_field_order_gcs_urls():
), "mime_type must come before file_uri in the file_data dict"
def test_gemini_files_api_uri_without_format():
"""
Test that Gemini Files API URIs work WITHOUT an explicit format/mime_type.
When a user uploads a file via the Gemini Files API and then references it
by URI (https://generativelanguage.googleapis.com/v1beta/files/...),
the file is already on Google's servers. These URLs return 403 when
fetched directly, so _process_gemini_media must NOT try to resolve the
MIME type via HTTP. Instead it should pass the URI through as file_data
and let the Gemini API resolve the type from its stored metadata.
Related issue: https://github.com/BerriAI/litellm/issues/24907
"""
from litellm.llms.vertex_ai.gemini.transformation import _process_gemini_media
file_url = "https://generativelanguage.googleapis.com/v1beta/files/37eh7rsw1vfe"
# Should NOT raise — previously this hit the generic https:// handler
# which called _get_image_mime_type_from_url() and got a 403.
result = _process_gemini_media(image_url=file_url)
assert "file_data" in result
file_data = result["file_data"]
assert file_data["file_uri"] == file_url
# When no format is provided, mime_type should be absent so the
# Gemini API infers it from the stored file metadata.
assert "mime_type" not in file_data
def test_gemini_files_api_uri_with_format():
"""
Test that Gemini Files API URIs correctly forward an explicit format.
Related issue: https://github.com/BerriAI/litellm/issues/24907
"""
from litellm.llms.vertex_ai.gemini.transformation import _process_gemini_media
file_url = "https://generativelanguage.googleapis.com/v1beta/files/n1vhxa28lyaw"
result = _process_gemini_media(image_url=file_url, format="text/plain")
assert "file_data" in result
file_data = result["file_data"]
assert file_data["file_uri"] == file_url
assert file_data["mime_type"] == "text/plain"
def test_extract_file_data_with_path_object():
"""
Test that filename is correctly extracted from Path objects for MIME type detection.