BerriAI/litellm · error · RefreshAccessTokenError

Refresh response missing fields: {data}

Error message

Refresh response missing fields: {data}

What it means

Raised as `RefreshAccessTokenError` (status 400) when the refresh endpoint returns HTTP 200 but the JSON lacks `access_token` or `id_token`. The server returned an unexpected payload (often an OAuth error object with 200); the raw body is included in the message.

Source

Thrown at litellm/llms/chatgpt/authenticator.py:316

                },
            )
            resp.raise_for_status()
            data: Final = resp.json()
        except httpx.HTTPStatusError as exc:
            raise RefreshAccessTokenError(
                message=f"Refresh token failed: {exc}",
                status_code=exc.response.status_code,
            )
        except Exception as exc:
            raise RefreshAccessTokenError(
                message=f"Refresh token failed: {exc}",
                status_code=400,
            )

        access_token: Final = data.get("access_token")
        id_token: Final = data.get("id_token")
        if not access_token or not id_token:
            raise RefreshAccessTokenError(
                message=f"Refresh response missing fields: {data}",
                status_code=400,
            )

        refreshed: Final = {
            "access_token": access_token,
            "refresh_token": data.get("refresh_token", refresh_token),
            "id_token": id_token,
        }
        auth_data: Final = self._build_auth_record(refreshed)
        self._write_auth_file(auth_data)
        return refreshed

    def _build_auth_record(self, tokens: dict[str, str]) -> dict[str, Any]:
        access_token: Final = tokens.get("access_token")
        id_token: Final = tokens.get("id_token")
        expires_at: Final = self._get_expires_at(access_token) if access_token else None
        account_id: Final = self._extract_account_id(id_token or access_token)

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Inspect the response body in the error message — an embedded `error` (e.g. `invalid_grant`) means the stored refresh token is dead; re-login.
  2. Give each deployment/process its own auth file to prevent refresh-token rotation races.
  3. Upgrade litellm for current refresh handling.
  4. Add monitoring on this error to detect forced logouts early.
Defensive patterns

Strategy: try-catch

Try / catch

try:
    resp = litellm.completion(model="chatgpt/gpt-4o", messages=msgs)
except litellm.AuthenticationError as e:
    if "Refresh response missing fields" in str(e) or "invalid_grant" in str(e):
        force_relogin()  # stored refresh token is consumed/dead
    raise

Prevention

When it happens

Trigger: Token refresh where OpenAI responds 200 with `{"error": "invalid_grant", ...}` or a partial payload missing the two required keys. Common when the refresh token was single-use and already consumed elsewhere.

Common situations: Two processes sharing one auth file: the first refresh rotates the token, the second sends the now-consumed token and gets an error body; OpenAI auth API contract drift; old litellm versions.

Related errors


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