{"record":{"id":"5b3fa8e0ae300dad","repo":"xtekky/gpt4free","slug":"no-refresh-token-found-in-credentials","errorCode":null,"errorMessage":"No refresh token found in credentials.","messagePattern":"No refresh token found in credentials\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"g4f/Provider/needs_auth/Antigravity.py","lineNumber":437,"sourceCode":"        if creds.get(\"project_id\"):\n            self._project_id = creds[\"project_id\"]\n\n        refresh_token = creds.get(\"refresh_token\")\n        access_token = creds.get(\"access_token\")\n        expiry_date = creds.get(\"expiry_date\")  # milliseconds since epoch\n\n        # Use original access token if still valid\n        if access_token and expiry_date:\n            expires_at = expiry_date / 1000\n            if expires_at - now > self.TOKEN_BUFFER_TIME:\n                self._access_token = access_token\n                self._expiry = expires_at\n                await self._cache_token(access_token, expiry_date)\n                return\n\n        # Otherwise, refresh token\n        if not refresh_token:\n            raise RuntimeError(\"No refresh token found in credentials.\")\n\n        await self._refresh_and_cache_token(refresh_token)\n\n    async def _refresh_and_cache_token(self, refresh_token: str) -> None:\n        \"\"\"Refresh the OAuth token and cache it.\"\"\"\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:","sourceCodeStart":419,"sourceCodeEnd":455,"githubUrl":"https://github.com/xtekky/gpt4free/blob/973504e1770928ed5fb82f43da528f441ad9ddc3/g4f/Provider/needs_auth/Antigravity.py#L419-L455","documentation":"RuntimeError raised near the end of Antigravity token bootstrap: credentials WERE found, but they contain no refresh_token, and the stored access_token is either absent or already expired (checked against expiry_date with TOKEN_BUFFER_TIME headroom). Without a refresh token there is no way to mint a new access token, so the flow refuses instead of sending doomed requests.","triggerScenarios":"Credentials JSON containing only an expired/missing-expiry access_token (partial export of an OAuth session); a service-account JSON of the wrong type (API-key-style JSON with no OAuth fields); users stripping the refresh_token for 'safety' before saving; cached token files from an older schema that stored tokens under different keys.","commonSituations":"Reusing an old cache file after the upstream auth flow changed token persistence; copying only part of the OAuth JSON into ANTIGRAVITY_SERVICE_ACCOUNT; long-lived deployments where the single access token (1h TTL) expired and no refresh token was ever stored.","solutions":["Re-run the interactive Antigravity OAuth login to obtain fresh credentials that include a refresh_token.","Inspect the credentials JSON/file and confirm a top-level \"refresh_token\" field exists (not nested or renamed).","If building the service-account JSON manually, include the full OAuth payload: refresh_token (required), plus access_token/expiry_date for a warm start.","Delete the stale cache file so the next run cannot reuse tokenless credentials."],"exampleFix":"# before: {\"access_token\":\"...\",\"expiry_date\":1700000000000}  -> expired, no refresh\n\n# after: {\"refresh_token\":\"1//0g...\",\"access_token\":\"...\",\"expiry_date\":...}","handlingStrategy":"validation","validationCode":"creds = json.loads(Path(creds_path).read_text())\nif not creds.get('refresh_token'):\n    raise SystemExit('Credentials lack refresh_token — re-run the Antigravity OAuth login')","typeGuard":"def has_refresh_token(creds: dict) -> bool:\n    return isinstance(creds.get('refresh_token'), str) and bool(creds['refresh_token'])","tryCatchPattern":"try:\n    ...\nexcept RuntimeError as e:\n    if 'No refresh token' in str(e):\n        # unrecoverable offline: only interactive re-login can fix\n        raise PermissionError('Re-authenticate with Antigravity to obtain a refresh token') from e\n    raise","preventionTips":["Always store the full OAuth payload including refresh_token when persisting credentials.","Validate credential shape (refresh_token present) before entering the token-refresh path.","Refresh tokens before expiry via _refresh_and_cache_token rather than waiting for hard failure."],"tags":["oauth","antigravity","token-refresh","credentials"],"backgroundTag":null,"analyzedSha":"973504e1770928ed5fb82f43da528f441ad9ddc3","analyzedAt":"2026-08-14T23:45:32.408Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}