{"record":{"id":"92fc31710accaa8d","repo":"xtekky/gpt4free","slug":"no-refresh-token-found-in-gcp-service-account","errorCode":null,"errorMessage":"No refresh token found in GCP_SERVICE_ACCOUNT.","messagePattern":"No refresh token found in GCP_SERVICE_ACCOUNT\\.","errorType":"validation","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"g4f/Provider/needs_auth/GeminiCLI.py","lineNumber":397,"sourceCode":"                raise RuntimeError(\"GCP_SERVICE_ACCOUNT environment variable not set.\")\n            creds = json.loads(self.env[\"GCP_SERVICE_ACCOUNT\"])\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 GCP_SERVICE_ACCOUNT.\")\n\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()","sourceCodeStart":379,"sourceCodeEnd":415,"githubUrl":"https://github.com/xtekky/gpt4free/blob/973504e1770928ed5fb82f43da528f441ad9ddc3/g4f/Provider/needs_auth/GeminiCLI.py#L379-L415","documentation":"Raised by AuthManager when the cached Google access token is missing or inside the TOKEN_BUFFER_TIME window and the credentials in GCP_SERVICE_ACCOUNT contain no refresh_token. The OAuth refresh flow at OAUTH_REFRESH_URL requires a refresh token to mint a new access token, so the auth manager cannot recover on its own. It means the stored credential set is incomplete, not merely expired.","triggerScenarios":"Calling GeminiCLI after the previously cached access token's expiry (expiry_date/1000 minus now) is below TOKEN_BUFFER_TIME, while the parsed GCP_SERVICE_ACCOUNT JSON has access_token/expiry_date but no non-empty refresh_token field.","commonSituations":"User pasted only the access_token portion of a token dump into GCP_SERVICE_ACCOUNT; token JSON produced by an older or external login tool that omits refresh_token; refresh token revoked or stripped when the Google OAuth consent was removed; KV token cache cleared so refresh is attempted for the first time.","solutions":["Re-run the GeminiCLI login flow (GeminiCLI.login / the g4f CLI auth command) to obtain a full token set, then store the returned JSON (access_token, refresh_token, expiry_date) in GCP_SERVICE_ACCOUNT","Verify the stored credential actually contains a refresh token: python -c \"import os,json;print(bool(json.loads(os.environ['GCP_SERVICE_ACCOUNT']).get('refresh_token')))\"","If the refresh token was revoked (Google Account > Security > Third-party access), revoke the app there and re-authorize","Make sure GCP_SERVICE_ACCOUNT is valid JSON and not truncated by shell quoting or newlines when exported"],"exampleFix":"// before\nexport GCP_SERVICE_ACCOUNT='{\"access_token\":\"ya29...\",\"expiry_date\":1750000000000}'\n\n// after\nexport GCP_SERVICE_ACCOUNT='{\"access_token\":\"ya29...\",\"refresh_token\":\"1//0g...\",\"expiry_date\":1750000000000}'","handlingStrategy":"validation","validationCode":"import json, os\n\ndef has_refresh_token() -> bool:\n    raw = os.environ.get(\"GCP_SERVICE_ACCOUNT\", \"\")\n    if not raw:\n        return False\n    try:\n        creds = json.loads(raw)\n    except json.JSONDecodeError:\n        return False\n    return bool(creds.get(\"refresh_token\"))\n\nif not has_refresh_token():\n    raise SystemExit(\"GCP_SERVICE_ACCOUNT lacks refresh_token; run GeminiCLI.login first\")","typeGuard":null,"tryCatchPattern":"try:\n    result = await GeminiCLI.create_async_generator(model, messages)\nexcept RuntimeError as e:\n    if \"No refresh token found\" in str(e):\n        await GeminiCLI.login()  # then retry once","preventionTips":["Always obtain GCP_SERVICE_ACCOUNT via the login flow, never hand-assemble it","Validate the JSON parses and contains refresh_token before starting the app","Keep the KV token cache warm so refresh is rarely needed"],"tags":["auth","oauth","gemini","credentials","configuration"],"backgroundTag":null,"analyzedSha":"973504e1770928ed5fb82f43da528f441ad9ddc3","analyzedAt":"2026-08-14T23:45:32.408Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}