[BUG] macOS CLI login loop: Keychain credentials blob has every accessToken/refreshToken blanked (claudeAiOauth + all mcpOAuth entries)
Preflight Checklist
- [x] I have searched existing issues — this is closely related to #83345 but on a different surface (CLI, not the VSCode extension) and with a broader corruption scope; filing separately per that difference, happy to have it merged.
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
Environment
- macOS 26.5.2 (build 25F84), Darwin 25.5.0, arm64
- Claude Code CLI 2.1.222,
installMethod: native(~/.local/bin/claude→~/.local/share/claude/versions/2.1.222) - Terminal: Terminal.app
- Sign-in method: Claude account via browser OAuth
ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN: unset. No proxy, no Bedrock/Vertex, noNODE_EXTRA_CA_CERTS.
What's wrong
claude in the terminal is stuck in a permanent login loop. Completing the browser OAuth flow never results in an authenticated session — running claude again immediately asks to sign in. Repeating the flow any number of times changes nothing.
Root cause observed
The macOS Keychain item Claude Code-credentials (in login.keychain-db) exists and is readable, parses as valid JSON, and has all of its non-secret fields intact — but every secret field in the entire blob is an empty string:
{
"claudeAiOauth": {
"accessToken": "", // EMPTY
"refreshToken": "", // EMPTY
"expiresAt": <in the past>, // EXPIRED
"refreshTokenExpiresAt": <number present>,
"subscriptionType": "<intact>",
"rateLimitTier": "<intact>"
},
"mcpOAuth": {
"<server-1>": { "accessToken": "", "serverName": "<intact>", "serverUrl": "<intact>",
"clientId": "<intact>", "redirectUri": "<intact>",
"discoveryState": { "oauthMetadataFound": true, ... } },
// ... same shape for all 7 stored MCP servers
}
}
All 7 mcpOAuth entries have accessToken: "" while their serverName, serverUrl, clientId, redirectUri, and discoveryState survived intact. One entry that stores a clientSecret retained a non-empty value for it.
This is the same corruption signature as #83345, but that report only observed the claudeAiOauth object via the VSCode extension. Here it affects every token-bearing field across the whole credential blob simultaneously, which suggests a write/serialization path that blanks token fields wholesale rather than an OAuth flow failing per-service.
Why this produces an infinite loop
- Startup finds the Keychain item present → does not report "no credentials", so no error surfaces.
- It reads
accessToken: ""→ cannot authenticate → prompts sign-in. - Browser OAuth completes, but the credential is never successfully written back.
- Next launch repeats from step 1, forever.
Supporting evidence that step 3 is a silent write failure: the Keychain item's modification date (mdat) is still 2026-08-02T17:28:51Z, unchanged despite many sign-in attempts on 2026-08-05. A successful write would have advanced it. The item is writable and the containing keychain is unlocked — reading the item non-interactively returns exit code 0 with no ACL prompt, so this is not a locked-keychain case.
Expected vs actual
- Expected: completing browser OAuth persists a valid token and the next
clauderun is authenticated. - Actual: the token is never persisted; the stale blob with empty tokens is retained indefinitely.
Suggested handling
- Validate on read. Treat a credential blob whose
accessTokenis empty/whitespace as absent rather than present, so the corrupt state can't masquerade as a valid entry. - Verify the write. After OAuth, read the credential back and fail loudly if it did not persist, instead of reporting success. (#79407 reports the same class of silent-write problem from a locked keychain.)
- Investigate the blanking itself. That non-secret sibling fields survive while every token field across both
claudeAiOauthand allmcpOAuthentries is emptied points at the serialization/merge step that rewrites the whole blob, not at the individual OAuth flows.
Related issues
- #83345 — same empty-token Keychain corruption, VSCode extension, v2.1.220 (
claudeAiOauthonly) - #79407 — macOS:
/loginfalsely reports success while the token write silently fails - #72875, #78946 — other reports of
/loginlooping
Workaround
Deleting the corrupted item and re-authenticating clears the loop:
security delete-generic-password -s "Claude Code-credentials"
This also discards all stored MCP OAuth grants, so every MCP server must be re-authorized afterward — though in this state their tokens were already blank.
3 Comments
Closing as a duplicate of #83345 — please follow and 👍 that issue instead so we can track it in one place. If you think this was closed in error, comment here and we'll reopen.
🤖 Generated with Claude Code
Not dupe because it didn't happen in VS code. Was trying to log in via CLI.
Reopened — thanks for the pushback, you're right that this isn't the same issue. Sorry for the noise.
🤖 Generated with Claude Code