{"record":{"id":"2a55c8983dacd77a","repo":"xtekky/gpt4free","slug":"token-refresh-failed-text-2a55c8","errorCode":null,"errorMessage":"Token refresh failed: {text}","messagePattern":"Token refresh failed: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"g4f/Provider/needs_auth/GeminiCLI.py","lineNumber":416,"sourceCode":"\n        await self._refresh_and_cache_token(refresh_token)\n\n    async def _refresh_and_cache_token(self, refresh_token: str) -> None:\n        headers = {\"Content-Type\": \"application/x-www-form-urlencoded\"}\n        data = {\n            \"client_id\": self.OAUTH_CLIENT_ID,\n            \"client_secret\": self.OAUTH_CLIENT_SECRET,\n            \"refresh_token\": refresh_token,\n            \"grant_type\": \"refresh_token\",\n        }\n\n        async with aiohttp.ClientSession() as session:\n            async with session.post(\n                self.OAUTH_REFRESH_URL, data=data, headers=headers\n            ) as resp:\n                if resp.status != 200:\n                    text = await resp.text()\n                    raise RuntimeError(f\"Token refresh failed: {text}\")\n                resp_data = await resp.json()\n                access_token = resp_data.get(\"access_token\")\n                expires_in = resp_data.get(\"expires_in\", 3600)  # seconds\n\n                if not access_token:\n                    raise RuntimeError(\"No access_token in refresh response.\")\n\n                self._access_token = access_token\n                self._expiry = time.time() + expires_in\n\n                expiry_date_ms = int(self._expiry * 1000)  # milliseconds\n\n                await self._cache_token(access_token, expiry_date_ms)\n\n    async def _cache_token(self, access_token: str, expiry_date: int) -> None:\n        # Cache token in KV store or fallback to memory cache\n        token_data = {\n            \"access_token\": access_token,","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/xtekky/gpt4free/blob/973504e1770928ed5fb82f43da528f441ad9ddc3/g4f/Provider/needs_auth/GeminiCLI.py#L398-L434","documentation":"The POST to Google's OAuth refresh endpoint (OAUTH_REFRESH_URL with grant_type=refresh_token) returned a non-200 status; the response body is embedded in the message. Typical bodies are invalid_grant (refresh token expired, revoked, or issued to a different client_id), invalid_client (wrong client_id/client_secret pair), or a redirect_uri mismatch.","triggerScenarios":"Any GeminiCLI request after the access token expires, when the refresh token is stale (Google refresh tokens for this OAuth client get invalidated), or when OAUTH_CLIENT_ID/OAUTH_CLIENT_SECRET constants no longer match the token that was issued.","commonSituations":"Refresh token revoked via the Google account security page or by issuing too many new tokens; g4f version bump changed the embedded OAuth client credentials so old refresh tokens no longer match; clock skew on the host; network proxy stripping the form-encoded POST.","solutions":["Read the embedded response body: 'invalid_grant' means re-login to mint a new refresh token; 'invalid_client' means the embedded client_id/secret are wrong for your token","Re-run the GeminiCLI login flow and replace GCP_SERVICE_ACCOUNT with the fresh token JSON","Upgrade g4f to the latest version so OAUTH_CLIENT_ID/OAUTH_CLIENT_SECRET match the ones the login flow uses","If behind a proxy, ensure POSTs to oauth2.googleapis.com are not modified (some MITM proxies break form encoding)"],"exampleFix":"try:\n    await provider.generate_async(...)\nexcept RuntimeError as e:\n    if \"Token refresh failed\" in str(e) and \"invalid_grant\" in str(e):\n        await GeminiCLI.login()  # mint a fresh refresh token","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    await provider.generate_async(...)\nexcept RuntimeError as e:\n    msg = str(e)\n    if \"Token refresh failed\" in msg:\n        if \"invalid_grant\" in msg:\n            await GeminiCLI.login()          # refresh token dead: re-auth\n        else:\n            await asyncio.sleep(5)           # transient: retry once\n            await provider.generate_async(...)","preventionTips":["Treat invalid_grant as terminal for the stored token and re-login immediately","Log the embedded response body — it names the exact OAuth error","Keep g4f updated so the embedded OAuth client matches the login flow"],"tags":["auth","oauth","gemini","http","token-refresh"],"backgroundTag":null,"analyzedSha":"973504e1770928ed5fb82f43da528f441ad9ddc3","analyzedAt":"2026-08-14T23:45:32.408Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}