xtekky/gpt4free · error · RuntimeError

Missing tokens in response

Error message

Missing tokens in response

What it means

Google's token endpoint returned HTTP 200 but the JSON was missing access_token or refresh_token. A successful code exchange always returns both (refresh_token because the flow requests offline access), so this indicates an anomalous response: an intercepted or HTML body, or an error-shaped 200.

Source

Thrown at g4f/Provider/needs_auth/GeminiCLI.py:1153

            }

            async with session.post(
                "https://oauth2.googleapis.com/token",
                data=token_data,
                headers={"Content-Type": "application/x-www-form-urlencoded"},
            ) as resp:
                if not resp.ok:
                    error_text = await resp.text()
                    raise RuntimeError(f"Token exchange failed: {error_text}")

                token_response = await resp.json()

            access_token = token_response.get("access_token")
            refresh_token = token_response.get("refresh_token")
            expires_in = token_response.get("expires_in", 3600)

            if not access_token or not refresh_token:
                raise RuntimeError("Missing tokens in response")

            # Get user info
            email = None
            async with session.get(
                "https://www.googleapis.com/oauth2/v1/userinfo?alt=json",
                headers={"Authorization": f"Bearer {access_token}"},
            ) as resp:
                if resp.ok:
                    user_info = await resp.json()
                    email = user_info.get("email")

        expires_at = int((start_time + expires_in) * 1000)  # milliseconds

        return {
            "access_token": access_token,
            "refresh_token": refresh_token,
            "expiry_date": expires_at,
            "email": email,

View on GitHub (pinned to 973504e177)

Solutions

  1. Log the raw token_response to inspect what was actually returned
  2. Retry with a brand-new authorization code from a fresh login attempt
  3. Check for proxy or captive-portal interference on oauth2.googleapis.com
  4. Update g4f if the token endpoint contract changed
Defensive patterns

Strategy: try-catch

Try / catch

try:
    tokens = await GeminiCLI.exchange_code_for_tokens(code, state)
except RuntimeError as e:
    if "Missing tokens in response" in str(e):
        # anomalous 200; restart the whole login flow
        tokens = await GeminiCLI.login()

Prevention

When it happens

Trigger: token_response.get('access_token') or token_response.get('refresh_token') is falsy after a 200 from oauth2.googleapis.com/token in exchange_code_for_tokens.

Common situations: Proxy or TLS interception replacing the response; an API behavior change; a rare Google-side anomaly.

Related errors


AI-assisted analysis of xtekky/gpt4free@973504e177 (2026-08-14). Data as JSON: /api/errors/569a9cc7fc0718dd. Report an issue: GitHub.