Login reports success but never persists the credential (macOS Keychain) — silent auth loop on 2.1.223 and 2.1.232

Status Open
Reported on v2.1.223
Maintainer reply None cached
Activity 1 comment · opened Aug 16, 2026

Summary

claude login completes browser OAuth and prints Login successful, but the credential is never written to the macOS Keychain. The next command reports not-logged-in, so every session demands a fresh login and every login silently persists nothing. Reproduced 4 times across two versions.

The most useful diagnostic detail: the token demonstrably works in-process before the write fails (evidence 3 below), which narrows this to the credential-persistence step rather than the OAuth exchange.

Environment

  • macOS 15.x (Darwin 25.5.0), Apple Silicon
  • Claude Code 2.1.223, then 2.1.232 after a clean npm i -g @anthropic-ai/claude-code — identical behavior on both
  • zsh; default login keychain, unlocked; standard keychain search list
  • Subscription account (not API billing)

Steps to reproduce

  1. claude login, complete the browser OAuth flow
  2. Observe Login successful
  3. Run any command, or claude login again → reports not logged in
  4. security find-generic-password -s "Claude Code-credentials" → item not found

Evidence

  1. No Keychain item is ever created. security find-generic-password -s "Claude Code-credentials" returns "could not be found" after every successful login. Prior history: a stale item from February existed with its modification date frozen months in the past, meaning logins were "succeeding" without ever touching it. That item was deleted during diagnosis; since then no item is created at all.
  2. No file fallback. ~/.claude/.credentials.json does not exist after login.
  3. The token authenticates in-process, then fails only at persistence. ~/.claude.json's oauthAccount block carries a profileFetchedAt timestamp from the failing login session itself, with fully refreshed profile fields (billing type, account and org identifiers, display name). So the process exchanged the OAuth code, made a successful authenticated API call, and wrote the non-secret results to config — only the secret write never happened, and without an error.
  4. The Keychain itself is healthy. From the same user in the same terminal: security add-generic-password -s probe -a probe -w x → write OK, read-back OK, delete OK.
  5. Nothing is diverting auth. No apiKeyHelper, no primaryApiKey, no ANTHROPIC_* variables in settings.json, settings.local.json, .zshrc, .zshenv, .zprofile, or /etc/zshenv. Config file is 0600 and user-owned.
  6. Possibly related, seen in the same session: an Auto-update failed toast suggesting claude doctor or a global npm reinstall.

Expected

Login successful should imply the credential was durably stored — or the CLI should surface an explicit error when the Keychain write fails, rather than reporting success and reading back nothing.

Impact

OAuth login is effectively unusable on this machine. A knock-on effect worth flagging: because there is no persisted credential and no file fallback, any non-interactive invocation loses authentication entirely — launchd agents, cron jobs, and CI wrappers that spawn claude inherit no shell profile, so they cannot pick up an environment-variable workaround either. Automation that previously worked via the Keychain silently stops authenticating.

Workaround

claude setup-token works and stays on subscription billing: it returns a long-lived token to the terminal, which can be exported as CLAUDE_CODE_OAUTH_TOKEN. Verified working here via a headless round-trip. Note this only helps processes that can see the variable — see the automation caveat above.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗