BerriAI/litellm · error · GigaChatError

Invalid JSON response: {raw_response.text}

Error message

Invalid JSON response: {raw_response.text}

What it means

Raised by GigaChat's transform_response when raw_response.json() throws for any reason — the completion endpoint returned a body that is not valid JSON. The GigaChatError reuses the upstream status_code and puts raw_response.text in the message, so you can see whether you got an HTML error page, a plain-text 502 from a load balancer, or an empty body. Note the bare `except Exception` means JSONDecodeError and any content-decoding error both land here.

Source

Thrown at litellm/llms/gigachat/chat/transformation.py:402

    def transform_response(
        self,
        model: str,
        raw_response: httpx.Response,
        model_response: ModelResponse,
        logging_obj: LiteLLMLoggingObj,
        request_data: dict,
        messages: list[AllMessageValues],
        optional_params: dict,
        litellm_params: dict,
        encoding: Any,
        api_key: str | None = None,
        json_mode: bool | None = None,
    ) -> ModelResponse:
        """Transform GigaChat response to OpenAI format."""
        try:
            response_json: Final = raw_response.json()
        except Exception:
            raise GigaChatError(
                status_code=raw_response.status_code,
                message=f"Invalid JSON response: {raw_response.text}",
            )

        is_structured_output: Final = optional_params.get("_structured_output", False)

        choices: Final = []
        for choice in response_json.get("choices", []):
            message_data = choice.get("message", {})
            finish_reason = choice.get("finish_reason", "stop")

            # Transform function_call to tool_calls or content
            if finish_reason == "function_call" and message_data.get("function_call"):
                func_call = message_data["function_call"]
                args = func_call.get("arguments", {})

                if is_structured_output:
                    # Convert to content for structured output

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Read the raw body embedded in the error message — HTML titles like '403 Forbidden' or 'Maintenance' identify the interceptor immediately.
  2. If a proxy sits in the path, bypass or allowlist the GigaChat hosts so JSON responses pass through untouched.
  3. Confirm GIGACHAT_API_BASE is correct (default https://gigachat.devices.sberbank.ru/api/v1) and not pointing at an HTML-serving host.
  4. Retry transiently — truncated bodies from dropped connections resolve on retry; persistent HTML responses indicate configuration/interception, not flakiness.

Example fix

# before: proxy returns HTML, completion fails with 'Invalid JSON response: <html>...'
os.environ["HTTPS_PROXY"] = "http://corp-proxy:3128"
r = litellm.completion(model="gigachat/GigaChat-Pro", messages=[...])

# after: exclude GigaChat hosts from the proxy
os.environ["NO_PROXY"] = "gigachat.devices.sberbank.ru,ngw.devices.sberbank.ru"
r = litellm.completion(model="gigachat/GigaChat-Pro", messages=[...])
Defensive patterns

Strategy: try-catch

Validate before calling

import os

# Best pre-check: a canary completion whose response body must be JSON
# (cheap guard for proxy/WAF interference before real traffic)
resp = litellm.completion(
    model="gigachat/GigaChat-Lite",
    messages=[{"role": "user", "content": "ping"}],
)
assert resp.choices, "non-JSON/intercepted response would have thrown GigaChatError at transform time"

Try / catch

from litellm.exceptions import APIError

try:
    resp = litellm.completion(model="gigachat/GigaChat-Pro", messages=msgs)
except APIError as e:
    msg = str(e)
    if "Invalid JSON response" in msg:
        if "<html" in msg.lower():
            raise RuntimeError("HTML body received — proxy/WAF intercepting GigaChat traffic; check NO_PROXY") from e
        raise RuntimeError("GigaChat returned non-JSON body; inspect embedded text and retry once") from e
    raise

Prevention

When it happens

Trigger: POST to the GigaChat completion API returns non-JSON: an HTML block page from a corporate proxy/WAF (often with HTTP 200 or 403), a gateway 502/504 HTML page, a truncated body from a dropped connection, or a text error like 'rate limit exceeded' served without JSON. Also occurs when custom GIGACHAT_API_BASE points at a non-GigaChat server.

Common situations: TLS-intercepting corporate proxies replacing responses; SberCloud/GigaChat maintenance windows returning HTML status pages; slow streaming responses cut off mid-body; api_base typos hitting an unrelated web server that returns HTML 404s.

Understand the failure class

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/96fe10b3f4490b8c. Report an issue: GitHub.