xtekky/gpt4free · error · RuntimeError
Missing tokens in response
Error message
Missing tokens in response
What it means
The token endpoint returned 200 but the JSON lacked access_token or refresh_token. access_token absence is anomalous (see error 101), but a missing refresh_token is the classic Google OAuth outcome when the consent grant did not request/force offline access — Google only issues a refresh_token the first time the user consents, unless approval_prompt=force / prompt=consent is used.
Source
Thrown at g4f/Provider/needs_auth/Antigravity.py:657
"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()
email = user_info.get("email")
# Discover project ID if not provided
effective_project_id = project_id
if not effective_project_id:
effective_project_id = await cls._fetch_project_id(
session, access_token
)
View on GitHub (pinned to 973504e177)
Solutions
- Revoke the app in Google Account > Security > Third-party access, then re-run login so Google issues a fresh refresh_token.
- Ensure the authorization URL includes access_type=offline and prompt=consent (as built by build_authorization_url) so a refresh token is always returned.
- Do not reuse an authorization code from an earlier session; start a new flow.
Example fix
# before: silent re-consent returns no refresh_token
url = "https://accounts.google.com/o/oauth2/v2/auth?...&response_type=code" # no offline access
# after: force offline access and fresh consent
url = ("https://accounts.google.com/o/oauth2/v2/auth?"
"...&response_type=code&access_type=offline&prompt=consent") Defensive patterns
Strategy: try-catch
Try / catch
try:
tokens = await Antigravity.exchange_code_for_tokens(code, state)
except RuntimeError as e:
if "Missing tokens in response" in str(e):
# most often missing refresh_token: revoke app grant, re-login with prompt=consent
raise Prevention
- Authorize with access_type=offline and prompt=consent so Google always returns a refresh_token
- Revoke the app in Google Account settings before re-linking to force a fresh refresh_token
- Validate the parsed token_response contains both keys before persisting credentials
When it happens
Trigger: Authorization request built without access_type=offline or without forcing re-consent, so Google returns only an access token; or the user had previously granted and Google suppresses the refresh_token on subsequent grants.
Common situations: Re-running login after revoking only the token (not the app grant) in the Google account; authorization URL parameters altered to drop access_type=offline; SSO/Workspace policies restricting offline access.
Related errors
- Token refresh failed: {text}
- Token exchange failed: {error_text}
- OAuth error: {OAuthCallbackHandler.callback_error}
- Failed to read OAuth credentials from {path}: {e}
- No refresh token found in credentials.
AI-assisted analysis of xtekky/gpt4free@973504e177 (2026-08-14).
Data as JSON: /api/errors/3e3cce725fb782a9.
Report an issue: GitHub.