xtekky/gpt4free · error · RuntimeError

Token exchange failed: {error_text}

Error message

Token exchange failed: {error_text}

What it means

The authorization-code-for-token exchange POST to https://oauth2.googleapis.com/token returned a non-ok status. The raw Google error body (JSON with error/error_description such as 'invalid_grant', 'redirect_uri_mismatch', 'invalid_client') is embedded in the message.

Source

Thrown at g4f/Provider/needs_auth/Antigravity.py:648

                "client_id": cls.OAUTH_CLIENT_ID,
                "client_secret": cls.OAUTH_CLIENT_SECRET,
                "code": code,
                "grant_type": "authorization_code",
                "redirect_uri": ANTIGRAVITY_REDIRECT_URI,
                "code_verifier": verifier,
            }

            async with session.post(
                "https://oauth2.googleapis.com/token",
                data=token_data,
                headers={
                    "Content-Type": "application/x-www-form-urlencoded",
                    "User-Agent": "google-api-nodejs-client/10.3.0",
                },
            ) 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()

View on GitHub (pinned to 973504e177)

Solutions

  1. Parse error_text: 'invalid_grant' -> code expired/used; restart the login flow for a fresh code. 'redirect_uri_mismatch' -> ensure the exact ANTIGRAVITY_REDIRECT_URI (localhost callback) is used and not rewritten by a proxy.
  2. Do not retry the exchange with the same code; authorization codes are single-use.
  3. Complete the browser consent promptly after generating the auth URL.
  4. Ensure no browser extension or proxy strips the state/code query parameters from the localhost redirect.
Defensive patterns

Strategy: try-catch

Try / catch

try:
    tokens = await Antigravity.exchange_code_for_tokens(code, state)
except RuntimeError as e:
    if "Token exchange failed" in str(e):
        # do NOT reuse `code`; generate a new authorization URL and retry once
        raise

Prevention

When it happens

Trigger: POST with grant_type=authorization_code, code, redirect_uri=ANTIGRAVITY_REDIRECT_URI, code_verifier. Fails with: expired or already-used authorization code (codes are single-use and ~10 min TTL), redirect_uri mismatch against the Google OAuth app config, wrong PKCE verifier, or bad client_id/client_secret.

Common situations: User took too long between opening the auth URL and completing consent; the callback fired twice and the code was consumed; the redirect URI recorded in Google's console differs from ANTIGRAVITY_REDIRECT_URI; retrying the exchange after a network error reused the same code.

Related errors


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