fix: treat a blank Claude credential as an expired login - #459
Conversation
Claude Code blanks accessToken/refreshToken in place when its login expires instead of removing the credential entry, so a null token is ambiguous: never signed in, or signed in and expired. Reporting configured:false for both hides the Claude section entirely while usage bars keep updating, which reads as an unsupported plan rather than an expired login. Reuse detectClaudeCodeCredentialsPresence() (existence-only, reads no secret) to tell the two apart and route the expired case into the reauth path added in xiufengsun#330, sharing the 401 handler's message. No new user-facing strings.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe change exposes Claude credential-presence detection and uses it to classify blank-token credentials as configured but requiring reauthentication. Missing credentials remain unconfigured. Tests cover both states. ChangesClaude authentication state
Estimated code review effort: 2 (Simple) | ~10 minutes Mergeability Score: ⚪ Minimal · up to The change is localized to expired Claude credential handling and its tests; no actionable merge-blocking risk remains after normal checks and review. Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@test/usage-limits.test.js`:
- Around line 1715-1735: Update the test around getUsageLimits to track calls to
the Anthropic usage URL instead of relying solely on fetchImpl throwing, then
assert that the tracked call count is zero after the result assertions. Preserve
the existing rejection/error assertions while explicitly verifying the Claude
usage API was never invoked.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 79d275a5-5257-4ce4-8c7e-cda106e4cfb7
📒 Files selected for processing (3)
src/lib/subscriptions.jssrc/lib/usage-limits.jstest/usage-limits.test.js
Throwing inside fetchImpl was not an assertion: provider failures are gathered with allSettled and the blank-credential branch never reads claudeResult, so a forbidden call would be swallowed and the test would still pass.
Why
When a Claude Code login expires, the credential entry is not removed — the secrets are blanked in place. The macOS Keychain item
Claude Code-credentialskeepsclaudeAiOauthwithaccessToken: "",refreshToken: "",expiresAt: 0, and all of the metadata (scopes,subscriptionType,rateLimitTier) intact.readClaudeCodeAccessToken()normalizes that empty string tonull, sogetUsageLimits()reports{ configured: false }and the dashboard hides the Claude section completely — the same rendering as a machine that never installed Claude Code. Meanwhile the usage bars keep updating (parsed from local logs, no auth needed), so the failure reads as "TokenTracker doesn't support my plan" instead of "your CLI login expired". Nothing is logged, andclaude-usage-limits-cache.jsonis never written.#330already solved the equivalent case on the wire: a 401 setsauth_action_required: "reauth", which the panel renders as a "Re-auth" badge with arun \claude`` tooltip. The blank-credential case never gets there, because there is no token to send.Fixes #458
What
!claudeTokenbranch by reusingdetectClaudeCodeCredentialsPresence(), which already exists and is existence-only (it reads no secret — on macOS it probes the Keychain without fetching the password).configured: true+auth_action_required: "reauth"+ the same message the 401 path uses, now shared asCLAUDE_AUTH_EXPIRED_MESSAGE.configured: false, so machines without Claude Code are unaffected.No new user-facing strings —
limits.reauth.badgeandlimits.reauth.tooltipalready cover the rendering, andREAUTH_CLI_COMMANDSalready mapsclaude.Tests
Two cases added to
test/usage-limits.test.js:configured: true,auth_action_required: "reauth", and the usage API is never called (the fakefetchImplthrows if it is);configured: false, no reauth flag.The first fails on
main(configured: false, no flag) and passes with this change. Full suite:npm test.Summary by CodeRabbit