xtekky/gpt4free · error · RuntimeError

No refresh token found in GCP_SERVICE_ACCOUNT.

Error message

No refresh token found in GCP_SERVICE_ACCOUNT.

What it means

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.

Source

Thrown at g4f/Provider/needs_auth/GeminiCLI.py:397

                raise RuntimeError("GCP_SERVICE_ACCOUNT environment variable not set.")
            creds = json.loads(self.env["GCP_SERVICE_ACCOUNT"])

        refresh_token = creds.get("refresh_token")
        access_token = creds.get("access_token")
        expiry_date = creds.get("expiry_date")  # milliseconds since epoch

        # Use original access token if still valid
        if access_token and expiry_date:
            expires_at = expiry_date / 1000
            if expires_at - now > self.TOKEN_BUFFER_TIME:
                self._access_token = access_token
                self._expiry = expires_at
                await self._cache_token(access_token, expiry_date)
                return

        # Otherwise, refresh token
        if not refresh_token:
            raise RuntimeError("No refresh token found in GCP_SERVICE_ACCOUNT.")

        await self._refresh_and_cache_token(refresh_token)

    async def _refresh_and_cache_token(self, refresh_token: str) -> None:
        headers = {"Content-Type": "application/x-www-form-urlencoded"}
        data = {
            "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()

View on GitHub (pinned to 973504e177)

Solutions

  1. 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
  2. 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')))"
  3. If the refresh token was revoked (Google Account > Security > Third-party access), revoke the app there and re-authorize
  4. Make sure GCP_SERVICE_ACCOUNT is valid JSON and not truncated by shell quoting or newlines when exported

Example fix

// before
export GCP_SERVICE_ACCOUNT='{"access_token":"ya29...","expiry_date":1750000000000}'

// after
export GCP_SERVICE_ACCOUNT='{"access_token":"ya29...","refresh_token":"1//0g...","expiry_date":1750000000000}'
Defensive patterns

Strategy: validation

Validate before calling

import json, os

def has_refresh_token() -> bool:
    raw = os.environ.get("GCP_SERVICE_ACCOUNT", "")
    if not raw:
        return False
    try:
        creds = json.loads(raw)
    except json.JSONDecodeError:
        return False
    return bool(creds.get("refresh_token"))

if not has_refresh_token():
    raise SystemExit("GCP_SERVICE_ACCOUNT lacks refresh_token; run GeminiCLI.login first")

Try / catch

try:
    result = await GeminiCLI.create_async_generator(model, messages)
except RuntimeError as e:
    if "No refresh token found" in str(e):
        await GeminiCLI.login()  # then retry once

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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