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
Problem
When Google OAuth refreshes an access token, the
tokensevent payload only contains the newaccess_token— it does not resend therefresh_token. However, bothOAuthCredentialStorage.saveCredentials()and the file-basedcacheCredentials()overwrite the stored credentials entirely, wiping out the existing refresh token.This causes sessions to fail with
API Error: No refresh token is setafter the first access token expiry (~1 hour).Additionally
deleteCredentials()in bothFileTokenStorageandKeychainTokenStoragethrows 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
No refresh token is setExpected behavior
Environment
GEMINI_FORCE_ENCRYPTED_FILE_STORAGE=trueOAuthCredentialStorage) and file-based (cacheCredentials) storage paths