xtekky/gpt4free · error · RuntimeError
Token exchange failed: {error_text}
Error message
Token exchange failed: {error_text} What it means
The POST to https://oauth2.googleapis.com/token exchanging the authorization code returned a non-OK status; the error body is embedded. Common bodies: invalid_grant (code already used or expired — codes are single-use and live roughly 10 minutes), mismatching code_verifier, or redirect_uri mismatch with GEMINICLI_REDIRECT_URI.
Source
Thrown at g4f/Provider/needs_auth/GeminiCLI.py:1144
async with aiohttp.ClientSession() as session:
token_data = {
"client_id": AuthManager.OAUTH_CLIENT_ID,
"client_secret": AuthManager.OAUTH_CLIENT_SECRET,
"code": code,
"grant_type": "authorization_code",
"redirect_uri": GEMINICLI_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"},
) 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
- Start a fresh login attempt to get a new authorization code — codes are single-use
- Ensure the exact same redirect URI is used in both steps (use the built-in callback server, not a hand-edited URL)
- Check the embedded body: invalid_grant means stale code; mismatch means PKCE or redirect mismatch
- Complete the exchange promptly (codes expire in minutes)
Defensive patterns
Strategy: try-catch
Try / catch
try:
tokens = await GeminiCLI.exchange_code_for_tokens(code, state)
except RuntimeError as e:
msg = str(e)
if "Token exchange failed" in msg and "invalid_grant" in msg:
# code consumed or expired: only a fresh code helps
raise AuthRestart("restart login to get a new authorization code")
raise Prevention
- Exchange the code immediately after consent; codes are single-use and short-lived
- Never replay a code after a partial failure — restart the flow
- Use the built-in localhost callback so the code is captured exactly once
When it happens
Trigger: Exchanging an authorization code that was already consumed once, pasted a second time, or older than its short validity; PKCE verifier from the state does not match the challenge sent at authorization; redirect_uri differs between the auth request and token request.
Common situations: Browser pre-fetch or middleware consumes the code via the localhost callback before the manual path uses it; retrying after a failure with the same code; clock drift; copy/paste truncation of the code.
Related errors
- Token refresh failed: {text}
- Missing PKCE verifier in state parameter
- Missing tokens in response
- OAuth callback timed out
- No authorization code received
AI-assisted analysis of xtekky/gpt4free@973504e177 (2026-08-14).
Data as JSON: /api/errors/afb020e89a0c7a6c.
Report an issue: GitHub.