BerriAI/litellm · error · ValueError

Error parsing file upload response

Error message

Error parsing file upload response: {e}

What it means

Thrown by GoogleAIStudioFilesHandler.transform_create_file_response after a Gemini Files API upload request completes. The handler does raw_response.json(), builds GeminiCreateFilesResponseObject from response_json['file'], then reads uri, sizeBytes, createTime (parsed via time.strptime with '%Y-%m-%dT%H:%M:%S.%f%z') and displayName to build an OpenAIFileObject. Any failure in that chain (non-JSON body, missing key, Pydantic validation error, unparseable timestamp) is caught, logged with a full traceback via verbose_logger.exception, and re-raised wrapped in this ValueError.

Solutions

  1. Enable verbose logging (litellm.set_verbose=True or LITELLM_LOG=DEBUG) and read the verbose_logger.exception traceback plus the wrapped '{e}' to see exactly which field/step failed
  2. Inspect the raw response body: the logged exception shows whether the JSON lacks file.uri/sizeBytes/createTime/displayName or whether .json() itself failed
  3. If a proxy/gateway is set via api_base or GEMINI_API_BASE, bypass it or confirm it forwards Gemini's JSON responses unmodified
  4. Upgrade litellm to the latest patch release to pick up any parser fixes for Gemini Files API changes
  5. As a workaround, upload files with the google-genai SDK and pass the resulting file URI into the Gemini chat request

Example fix

# before
file_obj = litellm.create_file(file=open('doc.pdf', 'rb'), custom_llm_provider='gemini')
# ValueError: Error parsing file upload response: 'uri'

# after - surface the real cause and verify the payload shape first
import litellm
litellm.set_verbose = True  # traceback shows the underlying KeyError/validation error
try:
    file_obj = litellm.create_file(
        file=open('doc.pdf', 'rb'),
        custom_llm_provider='gemini',
        api_key=GEMINI_KEY,
    )
except ValueError as e:
    raise RuntimeError(f'Gemini upload failed, inspect verbose log: {e}') from e
Defensive patterns

Strategy: try-catch

Validate before calling

import litellm
from litellm.llms.gemini.common_utils import GeminiModelInfo

if not (litellm.params.api_key if hasattr(litellm, 'params') else None) and not GeminiModelInfo.get_api_key():
    raise RuntimeError('Set GEMINI_API_KEY/GOOGLE_API_KEY before uploading files')

Try / catch

try:
    f = litellm.create_file(file=fp, custom_llm_provider='gemini', api_key=KEY)
except ValueError as e:
    if 'Error parsing file upload response' in str(e):
        # {e} suffix carries the underlying KeyError/ValidationError/json error
        log.exception('gemini upload parse failed: %s', e)
        raise
    raise

Prevention

When it happens

Trigger: litellm.create_file(..., custom_llm_provider='gemini') where the upload endpoint returns an error payload instead of a 'file' object; a file object missing uri/sizeBytes/createTime/displayName; a createTime that does not match the strict '%Y-%m-%dT%H:%M:%S.%f%z' format (e.g. no fractional seconds or an unexpected timezone suffix); or HTML/plain-text from a proxy configured via api_base/GEMINI_API_BASE.

Common situations: Custom GEMINI_API_BASE pointing at a gateway that returns HTML error pages; free-tier quota or API-not-enabled errors returned after the upload handshake; Gemini Files API schema drift on newer litellm versions; large-file uploads that were cut off server-side, producing a truncated body.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/2ae7c3b4442840c5. Report an issue: GitHub.

Appendix: source

Thrown at litellm/llms/gemini/files/transformation.py:192

                id=response_object["uri"],  # Gemini uses URI as identifier
                bytes=int(response_object["sizeBytes"]),  # Gemini doesn't return file size
                created_at=int(
                    time.mktime(
                        time.strptime(
                            response_object["createTime"].replace("Z", "+00:00"),
                            "%Y-%m-%dT%H:%M:%S.%f%z",
                        )
                    )
                ),
                filename=response_object["displayName"],
                object="file",
                purpose="user_data",  # Default to assistants as that's the main use case
                status="uploaded",
                status_details=None,
            )
        except Exception as e:
            verbose_logger.exception("Error parsing file upload response: %s", e)
            raise ValueError(f"Error parsing file upload response: {e}")

    def transform_retrieve_file_request(
        self,
        file_id: str,
        optional_params: dict,
        litellm_params: dict,
    ) -> tuple[str, dict]:
        """
        Get the URL to retrieve a file from Google AI Studio.

        Endpoint:
        GET https://generativelanguage.googleapis.com/v1beta/{name=files/*}

        The URL should look like:
        https://generativelanguage.googleapis.com/v1beta/files/{file_id}?key=API_KEY

        We expect file_id to be just the file identifier (e.g., files/abc123 or abc123)
        as returned by the upload response. (If it's a full URL, extract the file name.)

View on GitHub (pinned to 77b7c6c40c)