BerriAI/litellm · error · GigaChatAuthError
Invalid token response: {data}
Error message
Invalid token response: {data} What it means
Raised inside _parse_token_response when the GigaChat OAuth endpoint returned HTTP 200 but the JSON body contains neither a 'tok' nor an 'access_token' field. litellm accepts both the native Sberbank shape ({tok, exp}) and an OAuth-style shape ({access_token, expires_at}); if both lookups are falsy it throws GigaChatAuthError(500) with the full parsed body in the message. This indicates an auth-endpoint contract change or an unexpected (possibly proxied/cached) 200 response.
Source
Thrown at litellm/llms/gigachat/authenticator.py:229
message=f"GigaChat authentication failed: {e.response.text}",
)
except httpx.RequestError as e:
raise GigaChatAuthError(
status_code=500,
message=f"GigaChat authentication request failed: {e}",
)
def _parse_token_response(response: httpx.Response) -> tuple[str, int]:
"""Parse OAuth token response."""
data: Final = response.json()
# GigaChat returns either 'tok'/'exp' or 'access_token'/'expires_at'
access_token: Final = data.get("tok") or data.get("access_token")
expires_at = data.get("exp") or data.get("expires_at")
if not access_token:
raise GigaChatAuthError(
status_code=500,
message=f"Invalid token response: {data}",
)
# expires_at is in milliseconds
if isinstance(expires_at, str):
expires_at = int(expires_at)
verbose_logger.debug("GigaChat access token obtained successfully")
return access_token, expires_at
View on GitHub (pinned to 6c2dcb801b)
Solutions
- Inspect the data dict printed in the error message — it shows exactly which fields the endpoint returned instead of tok/access_token.
- If using a custom GIGACHAT_API_BASE, verify the endpoint returns the Sberbank OAuth shape ({"tok": "...", "exp": <ms>}) or {"access_token": ..., "expires_at": ...}.
- Test the raw exchange directly: curl -X POST <auth_url> -H "Authorization: Basic <base64 key>" -d 'scope=GIGACHAT_API_PERS' and inspect the JSON.
- If the upstream format genuinely changed, pin/patch litellm's _parse_token_response or open an issue upstream with the response shape.
Example fix
# before: custom gateway returns {"accessToken": ...} -> litellm raises 'Invalid token response'
os.environ["GIGACHAT_API_BASE"] = "https://my-gateway.example.com"
# after: point at a gateway that passes through the native OAuth shape, or proxy-rewrite the field
# (gateway config: map "accessToken" -> "tok", "expiresInMs" -> "exp")
os.environ["GIGACHAT_API_BASE"] = "https://my-gateway.example.com" # now returns {"tok": ..., "exp": ...} Defensive patterns
Strategy: validation
Validate before calling
import base64, os, httpx
key = os.environ["GIGACHAT_API_KEY"]
scope = os.getenv("GIGACHAT_API_SCOPE", "GIGACHAT_API_PERS")
auth_url = os.getenv("GIGACHAT_API_BASE", "https://ngw.devices.sberbank.ru:9443") + "/api/v1/oauth"
resp = httpx.post(auth_url,
headers={"Authorization": f"Basic {key}", "Content-Type": "application/x-www-form-urlencoded"},
data={"scope": scope}, timeout=30, verify=False)
data = resp.json()
assert data.get("tok") or data.get("access_token"), f"Unexpected token response shape: {list(data)}" Try / catch
from litellm.exceptions import AuthenticationError
try:
litellm.completion(model="gigachat/GigaChat-Pro", messages=msgs)
except AuthenticationError as e:
if "Invalid token response" in str(e):
# body is embedded; use it to diagnose gateway/contract drift
raise RuntimeError(f"GigaChat auth endpoint returned unexpected JSON; inspect body in message: {e}") from e
raise Prevention
- If proxying GigaChat auth, preserve the tok/exp field names exactly.
- Pin litellm versions in production so contract changes surface in upgrades, not randomly.
- Add a startup canary that performs one token exchange and asserts tok/access_token is present.
- Keep the raw response body from the error message in incident reports — it names the divergence.
When it happens
Trigger: The POST to the auth URL succeeds with 2xx, but the body is e.g. an HTML login page served by a captive portal/proxy that httpx .json() happens to parse (or a JSON error envelope like {"status": 401, "message": ...} returned with 200), or GigaChat changed its token field names in an API revision. Also triggered when a custom GIGACHAT_API_BASE points at a gateway whose token response format differs from Sberbank's.
Common situations: Self-hosted API gateways or mock servers in front of GigaChat that return a different token schema; GigaChat API version drift between when the key was issued and now; a WAF rewriting the auth response; a custom api_base copied from a different integration tutorial.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- GigaChat authentication request failed: {e}
- Device code response missing fields: {data}
- Token exchange response missing fields: {data}
- Refresh response missing fields: {data}
- OAuth M2M token request failed: {e}
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/a5e4b7d38ed38d44.
Report an issue: GitHub.