{"record":{"id":"348ec733693cb1f0","repo":"BerriAI/litellm","slug":"raw-response-text-348ec7","errorCode":null,"errorMessage":"raw_response.text","messagePattern":"raw_response\\.text","errorType":"http","errorClass":"GeminiError","httpStatus":null,"severity":"error","filePath":"litellm/llms/gemini/interactions/transformation.py","lineNumber":244,"sourceCode":"                        request_body[\"response_format\"] = [existing_rf, image_rf]\n\n        return request_body\n\n    def transform_response(\n        self,\n        model: str | None,\n        raw_response: httpx.Response,\n        logging_obj: LiteLLMLoggingObj,\n    ) -> InteractionsAPIResponse:\n        \"\"\"Parse response - it already matches our response type.\"\"\"\n        try:\n            logging_obj.post_call(\n                original_response=raw_response.text,\n                additional_args={\"complete_input_dict\": {}},\n            )\n            raw_json: Final = raw_response.json()\n        except Exception:\n            raise GeminiError(\n                message=raw_response.text,\n                status_code=raw_response.status_code,\n                headers=dict(raw_response.headers),\n            )\n\n        verbose_logger.debug(\"Google AI Interactions response: %s\", raw_json)\n\n        response: Final = InteractionsAPIResponse(**raw_json)\n        response._hidden_params[\"headers\"] = dict(raw_response.headers)\n        response._hidden_params[\"additional_headers\"] = process_response_headers(dict(raw_response.headers))\n\n        return response\n\n    def transform_streaming_response(\n        self,\n        model: str | None,\n        parsed_chunk: dict,\n        logging_obj: LiteLLMLoggingObj,","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/llms/gemini/interactions/transformation.py#L226-L262","documentation":"Raised in GoogleAIStudioInteractionsConfig.transform_response when the response body cannot be processed: it first calls logging_obj.post_call with raw_response.text, then raw_response.json(). Any exception there (non-JSON body such as an HTML gateway error, an empty 5xx body, malformed JSON) triggers a GeminiError whose message is the VERBATIM response text, with the upstream status_code and headers attached. The odd message you see is simply the raw upstream payload.","triggerScenarios":"A proxy/gateway (custom api_base) returning HTML 502/504 pages; Cloudflare challenge/block pages; an empty body on a server error; region or model-access denials returning non-JSON; a streaming (?alt=sse) response being parsed by the non-streaming transform.","commonSituations":"Self-hosted LiteLLM proxy fronts Google with an LB that errors in HTML; corporate proxies injecting block pages; transient Google 5xxs; accidentally calling the non-streaming completion path with stream=True data.","solutions":["Read the GeminiError: message carries the raw upstream body, status_code and headers tell you who rejected it (502/504 → gateway; 429 → quota; 403 → access)","If message contains HTML, inspect it for the proxy product name and fix/bypass the proxy for generativelanguage.googleapis.com","For streaming requests, use the streaming code path (stream=True) so SSE bodies are parsed correctly","Retry transient 5xxs with backoff; make retries idempotent using previous_interaction_id where appropriate"],"exampleFix":"# before\nresp = litellm.interactions.create(model='gemini-2.5-flash', input=..., api_key=KEY)\n# GeminiError: <html><head><title>502 Bad Gateway</title>...\n\n# after - surface status and detect proxy HTML instead of crashing on the raw text\nfrom litellm.llms.gemini.common_utils import GeminiError\ntry:\n    resp = litellm.interactions.create(model='gemini-2.5-flash', input=..., api_key=KEY)\nexcept GeminiError as e:\n    if e.status_code in (502, 503, 504) or '<html' in (e.message or '').lower():\n        raise RuntimeError(f'Gateway error {e.status_code}, check api_base/proxy') from e\n    raise","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"from litellm.llms.gemini.common_utils import GeminiError\n\ntry:\n    resp = litellm.interactions.create(model='gemini-2.5-flash', input=inp, api_key=KEY)\nexcept GeminiError as e:\n    if e.status_code and e.status_code >= 500 or '<html' in (e.message or '').lower():\n        backoff_and_retry()  # gateway/transient — safe to retry\n    else:\n        raise  # 4xx: fix key/model/api_base; message holds the raw upstream body","preventionTips":["Treat an HTML message as a proxy/gateway fault: check api_base routing before blaming Google","Retry 5xx GeminiErrors with exponential backoff; do not retry 4xx","Use the streaming path for stream=True so SSE bodies never hit the JSON parser","Alert on status_code + headers from GeminiError — they identify the rejecting layer"],"tags":["gemini","interactions-api","response-parsing","non-json-response"],"backgroundTag":"non-json-api-response","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}