Fall back to legacy keychain when stale scoped Claude credentials 401#8767
Conversation
Claude Code maintains the legacy 'Claude Code-credentials' Keychain item for the default config dir, but a scoped item (service suffixed with sha256(configDir)) can be left behind by sessions that ran with CLAUDE_CONFIG_DIR set. Once its access token expires, nothing refreshes it: readFromKeychain returned the scoped token unconditionally, every usage fetch 401'd as 'stale-token', and system-default auth has no CLI recovery lane — so the status bar showed 'Refreshing sign-in' forever while the legacy item held a perfectly valid token. Two changes: - readFromKeychain: an actual access token from the legacy item now beats scoped refresh-only credentials (Orca cannot refresh tokens itself, so refresh-only must not shadow a working token). - On a stale-token OAuth failure with scoped-keychain credentials, retry once with the legacy item's token before classifying the failure. Host system-default auth only — managed/WSL credentials never fall back to the host user's legacy keychain item. The legacy retry runs before CLI repair, so a readable working token is preferred over launching a hidden 25s claude PTY.
The legacy-keychain retry must never answer a WSL target with the host user's keychain account, and must not double the usage request when the legacy item mirrors the failed scoped token. Add tests locking both.
📝 WalkthroughWalkthroughUpdated Claude rate-limit credential selection to prefer legacy access tokens when scoped tokens are unavailable. Added centralized OAuth success completion and conditional retries using legacy Keychain credentials after stale scoped-token failures. Added tests for fallback conditions, credential provenance, WSL behavior, mirrored tokens, disabled PTY fallback, and CLI repair sequencing. 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/main/rate-limits/claude-fetcher.ts (1)
692-720: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffBoth the source and test files grow further under pre-existing
max-linessuppressions. As per coding guidelines,**/*.{js,jsx,ts,tsx}files should "Never disablemax-linesvia ESLint or oxlint, including line-specific variants," but this PR adds substantial new code to two files that already carry such a disable comment.
src/main/rate-limits/claude-fetcher.ts#L692-L720: extractcompleteOAuthUsageSuccess,canRetryWithLegacyKeychainToken, andretryOAuthWithLegacyKeychainToken(and their related helpers) into a dedicated legacy-retry module rather than growing this file further.src/main/rate-limits/claude-fetcher.test.ts#L181-L343: move the new legacy-fallback test cases into a dedicated spec file alongside the extracted module.Source: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: c45fee64-d4ba-47f1-aefc-21de7217677e
📒 Files selected for processing (2)
src/main/rate-limits/claude-fetcher.test.tssrc/main/rate-limits/claude-fetcher.ts
Problem
Claude Code maintains the legacy
Claude Code-credentialsKeychain item for the default config dir, but a scoped item (Claude Code-credentials-<sha256(configDir)[:8]>) can be left behind by sessions that ran withCLAUDE_CONFIG_DIRset explicitly. Once that scoped item's access token expires, nothing ever refreshes it — andreadFromKeychainreturned the scoped token unconditionally (expiresAtis deliberately not authoritative), so:stale-token.Fix
readFromKeychain: an actual access token from the legacy item now beats scoped refresh-only credentials — Orca cannot refresh tokens itself, so refresh-only must not shadow a working token.stale-tokenOAuth failure withscoped-keychaincredentials, retry once with the legacy item's token before classifying the failure. Gated to host system-default auth only — managed/WSL credentials never fall back to the host user's legacy keychain item.claudePTY.Testing
vitest run src/main/rate-limits src/main/claude-accounts— 448 tests pass;typecheck:tsc:nodeclean.