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

  1. Log the full refresh response body to see what actually came back instead of an OAuth token JSON.
  2. Check for HTTP(S) proxy interference (HTTPS_PROXY env, captive portal) and bypass or trust it properly.
  3. Delete the cached credentials and re-authenticate from scratch.
  4. 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

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


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