{"record":{"id":"63c5661fd108f6b8","repo":"google-gemini/gemini-cli","slug":"failed-to-load-oauth-credentials","errorCode":null,"errorMessage":"Failed to load OAuth credentials","messagePattern":"Failed to load OAuth credentials","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/code_assist/oauth-credential-storage.ts","lineNumber":57,"sourceCode":"          scope: scope || undefined,\n        };\n\n        if (expiresAt) {\n          googleCreds.expiry_date = expiresAt;\n        }\n\n        return googleCreds;\n      }\n\n      // Fallback: Try to migrate from old file-based storage\n      return await this.migrateFromFileStorage();\n    } catch (error: unknown) {\n      coreEvents.emitFeedback(\n        'error',\n        'Failed to load OAuth credentials',\n        error,\n      );\n      throw new Error('Failed to load OAuth credentials', { cause: error });\n    }\n  }\n\n  /**\n   * Save OAuth credentials\n   */\n  static async saveCredentials(credentials: Credentials): Promise<void> {\n    if (!credentials.access_token) {\n      throw new Error('Attempted to save credentials without an access token.');\n    }\n\n    const existing = await this.storage.getCredentials(MAIN_ACCOUNT_KEY);\n    const mergedRefreshToken =\n      credentials.refresh_token || existing?.token.refreshToken;\n\n    // Convert Google Credentials to OAuthCredentials format\n    const mcpCredentials: OAuthCredentials = {\n      serverName: MAIN_ACCOUNT_KEY,","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/google-gemini/gemini-cli/blob/5024443c7217464a66e98f80d73172a26440bd8f/packages/core/src/code_assist/oauth-credential-storage.ts#L39-L75","documentation":"Thrown by OAuthCredentialStorage.loadCredentials() when the underlying HybridTokenStorage fails to read credentials. The method first tries the configured token storage (keychain-backed), then falls back to migrating from the old file-based storage at ~/.gemini/oauth_creds.json. If both paths throw, the error is wrapped with cause and re-thrown. The original error is preserved in the cause chain for diagnostics.","triggerScenarios":"Calling OAuthCredentialStorage.loadCredentials() when storage.getCredentials() throws (e.g., keychain access denied, locked keychain on macOS, or D-Bus/secret-service failure on Linux) AND migrateFromFileStorage() also throws (e.g., the old file is corrupt or unreadable).","commonSituations":"macOS keychain is locked or the process lacks Keychain access permissions; Linux secret-service (gnome-keyring) is not running or DBUS_SESSION_BUS_ADDRESS is unset; running inside a container or CI without a keyring daemon; the old credentials file exists but is corrupted; permission issues on ~/.gemini directory.","solutions":["On Linux, ensure gnome-keyring or kwallet is running and DBUS_SESSION_BUS_ADDRESS is exported.","On macOS, unlock the keychain or grant the terminal/IDE access to the 'gemini-cli-oauth' keychain item.","In headless/CI environments, use GEMINI_API_KEY or Application Default Credentials instead of OAuth.","If the stored credentials are corrupt, clear them (OAuthCredentialStorage.clearCredentials()) and re-authenticate.","Inspect error.cause for the underlying storage error to pinpoint keychain vs. file system issues."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Check environment suitability before attempting credential load\nfunction hasKeychainAccess(): boolean {\n  if (process.platform === 'linux') {\n    return !!process.env['DBUS_SESSION_BUS_ADDRESS'];\n  }\n  return true; // macOS/Windows keychain generally available\n}\n\nif (!hasKeychainAccess()) {\n  console.warn('No keychain available; set GEMINI_API_KEY for non-interactive auth.');\n}","typeGuard":null,"tryCatchPattern":"try {\n  const creds = await OAuthCredentialStorage.loadCredentials();\n} catch (e) {\n  if (e instanceof Error && e.message === 'Failed to load OAuth credentials') {\n    // Keychain unavailable — fall back to API key or re-auth\n    console.error('Credential storage inaccessible. Set GEMINI_API_KEY or re-authenticate.');\n    process.exit(1);\n  }\n  throw e;\n}","preventionTips":["Ensure a keyring daemon is running on Linux before using OAuth.","In CI/containers, use GEMINI_API_KEY or ADC instead of interactive OAuth.","Grant the terminal/IDE keychain access on macOS.","Periodically verify credential integrity and clear if corrupted."],"tags":["oauth","credentials","keychain","storage","environment"],"backgroundTag":null,"analyzedSha":"5024443c7217464a66e98f80d73172a26440bd8f","analyzedAt":"2026-08-12T06:01:53.711Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}