google-gemini/gemini-cli

OAuth refresh token lost during token refresh, causing 'No refresh token is set' after ~1 hour

Open

#21691 opened on Mar 9, 2026

View on GitHub
 (9 comments) (0 reactions) (1 assignee)TypeScript (103,992 stars) (13,657 forks)batch import
area/corearea/securityeffort/smallhelp wantedkind/bugpriority/p1status/bot-triaged

Description

Problem

When Google OAuth refreshes an access token, the tokens event payload only contains the new access_token — it does not resend the refresh_token. However, both OAuthCredentialStorage.saveCredentials() and the file-based cacheCredentials() overwrite the stored credentials entirely, wiping out the existing refresh token.

This causes sessions to fail with API Error: No refresh token is set after the first access token expiry (~1 hour).

Additionally

deleteCredentials() in both FileTokenStorage and KeychainTokenStorage throws an error when the credential doesn't exist (e.g., was already deleted). This causes a cascading "Failed to clear OAuth credentials" error loop when re-auth tries to clear credentials that are already gone, preventing users from re-authenticating.

Steps to reproduce

  1. Authenticate with Google OAuth
  2. Use the CLI for >1 hour (until access token expires)
  3. Send another message → No refresh token is set
  4. CLI attempts to clear credentials and re-auth → repeated "Failed to clear OAuth credentials" errors

Expected behavior

  • Refresh tokens should be preserved when saving a token refresh (merge with existing rather than overwrite)
  • Deleting non-existent credentials should be a no-op

Environment

  • macOS, using GEMINI_FORCE_ENCRYPTED_FILE_STORAGE=true
  • Affects both encrypted (OAuthCredentialStorage) and file-based (cacheCredentials) storage paths

Contributor guide