xtekky/gpt4free · critical · RuntimeError
No access_token in refresh response.
Error message
No access_token in refresh response.
What it means
Thrown when the OAuth refresh endpoint returns HTTP 200 but the JSON body has no 'access_token' field. A 200 without an access token is unusual for Google's token endpoint and typically means the response is not the expected OAuth payload (proxy interception, HTML error page parsed as JSON, or an API contract change).
Source
Thrown at g4f/Provider/needs_auth/Antigravity.py:463
"client_id": self.OAUTH_CLIENT_ID,
"client_secret": self.OAUTH_CLIENT_SECRET,
"refresh_token": refresh_token,
"grant_type": "refresh_token",
}
async with aiohttp.ClientSession() as session:
async with session.post(
self.OAUTH_REFRESH_URL, data=data, headers=headers
) as resp:
if resp.status != 200:
text = await resp.text()
raise RuntimeError(f"Token refresh failed: {text}")
resp_data = await resp.json()
access_token = resp_data.get("access_token")
expires_in = resp_data.get("expires_in", 3600) # seconds
if not access_token:
raise RuntimeError("No access_token in refresh response.")
self._access_token = access_token
self._expiry = time.time() + expires_in
expiry_date_ms = int(self._expiry * 1000) # milliseconds
await self._cache_token(access_token, expiry_date_ms)
async def _cache_token(self, access_token: str, expiry_date: int) -> None:
"""Cache token in memory."""
token_data = {
"access_token": access_token,
"expiry_date": expiry_date,
"cached_at": int(time.time() * 1000), # ms
}
self._token_cache[self.KV_TOKEN_KEY] = token_data
async def _get_cached_token(self) -> Optional[Dict[str, Any]]:
"""Return in-memory cached token if present and still valid."""View on GitHub (pinned to 973504e177)
Solutions
- Log the full refresh response body to see what actually came back instead of an OAuth token JSON.
- Check for HTTP(S) proxy interference (HTTPS_PROXY env, captive portal) and bypass or trust it properly.
- Delete the cached credentials and re-authenticate from scratch.
- If OAUTH_REFRESH_URL was overridden in a subclass, restore the official https://oauth2.googleapis.com/token endpoint.
Defensive patterns
Strategy: try-catch
Try / catch
try:
...
except RuntimeError as e:
if "No access_token in refresh response" in str(e):
# log raw response via debug logging; suspect proxy; re-login
await antigravity.login() Prevention
- Keep googleapis.com out of MITM/proxy rewriting paths
- Pin provider version so OAUTH_REFRESH_URL matches Google's contract
- Log refresh responses at debug level in production to catch shape changes early
When it happens
Trigger: session.post(OAUTH_REFRESH_URL, ...) returns 200, resp.json() succeeds, but resp_data.get('access_token') is empty/None. Happens with captive portals or corporate proxies returning 200 HTML pages, or a misconfigured OAUTH_REFRESH_URL pointing at the wrong endpoint.
Common situations: Corporate MITM proxy returns a 200 login page for googleapis.com; the endpoint constant was patched/overridden; Google returned an unexpected payload shape.
Related errors
- Token refresh failed: {text}
- GitHub Copilot OAuth not configured. Please run 'g4f auth gi
- NO_TOKEN
- OAuth error: {OAuthCallbackHandler.callback_error}
- Failed to read OAuth credentials from {path}: {e}
AI-assisted analysis of xtekky/gpt4free@973504e177 (2026-08-14).
Data as JSON: /api/errors/3a4114159f27cdc4.
Report an issue: GitHub.