OAuth refresh token lost during token refresh, causing 'No refresh token is set' after ~1 hour
#21691 opened on Mar 9, 2026
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
- Authenticate with Google OAuth
- Use the CLI for >1 hour (until access token expires)
- Send another message →
No refresh token is set - 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