[BUG] /status and the startup banner show the cached oauthAccount identity, not the account the session authenticates and bills (root cause + deterministic repro)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Claude Code's identity display surfaces — the /status account panel (Email, Organization) and the startup "Welcome back" banner — do not derive their identity from the credential the session actually uses. They read the login-time cache: the oauthAccount blob in .claude.json, written at /login and by a TTL-gated (roughly daily) profile refresh. The credential used for requests is resolved independently from secure storage (macOS: a Keychain entry whose service name derives from CLAUDE_SECURESTORAGE_CONFIG_DIR / CLAUDE_CONFIG_DIR; elsewhere: .credentials.json in the corresponding directory). Whenever cache and credential diverge, the display is wrong — the session authenticates, bills, and rate-limits against one account while /status names another.
In the minified binary (2.1.215) the panel builder reads Wl()?.emailAddress / Wl()?.organizationName, where function Wl(){return MS()?bt().oauthAccount:void 0} — the cached config blob; the startup banner reads the same accessor. The "Login method" line comes from the live credential/profile path, which is why it can be the only truthful field.
Open reports that match this mechanism from different setups, none identifying the cause: #74217 (/status wrong email/org, Enterprise seat billed), #78838 (desktop app injects the stale ~/.claude.json identity into session context; billing correct), #72552 (headless spawn resolves the default Keychain slot while claude auth status reports the cached metadata). Pointer comments left on the first two; filed fresh so the root cause is triageable in one place.
What Should Happen?
/status and the startup banner should display the account the session's active credential belongs to — or, at minimum, compare the cached oauthAccount against the credential store in use and warn on mismatch instead of presenting the cache as authoritative.
Error Messages/Logs
$ CLAUDE_SECURESTORAGE_CONFIG_DIR="$HOME/.claude-b-creds" claude # credential store = account B
# startup banner: "Welcome back ..." + accountA@example.com's Organization <-- account A (wrong)
/status
Version: 2.1.215
Login method: Claude Max account
Organization: accountA@example.com's Organization <-- cached account A (wrong)
Email: accountA@example.com <-- cached account A (wrong; session credential is account B)
Steps to Reproduce
CLAUDE_SECURESTORAGE_CONFIG_DIR="$HOME/.claude-b-creds" claude→/loginas account B (creates a separate credential store).- Plain
claude→/loginas account A (default store). The shared.claude.jsonoauthAccountnow holds A. CLAUDE_SECURESTORAGE_CONFIG_DIR="$HOME/.claude-b-creds" claude→ the session authenticates and bills B, but the banner and/statusshow A's email/organization.
Reproduced on 2.1.215 (macOS, 2026-07-19). One timing note: run step 3 within ~24 h of step 2 — a TTL-gated profile refresh (24 h) rewrites the cache from the live session's credential, so a long-stale mismatch can self-correct. The display follows whichever account last wrote oauthAccount into the shared .claude.json; the billing follows the credential store the session resolved.
Claude Model
Other
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.215 (Claude Code)
Platform
Other
Operating System
macOS
Terminal/Shell
Other
Additional Information
- The same last-writer-wins cache also explains why the mismatch direction can flip:
/loginwrites it unconditionally, and the daily profile refresh writes it with the live session's identity. CLAUDE_SECURESTORAGE_CONFIG_DIRis undocumented but widely used by third-party multi-account tooling; a separate documentation request covers it: #79223.- On the desktop-app surface, #78838 (comment 2) found the app separately records the true active account (
lastKnownAccountUuidin its own config) — a locally available source of truth the display/context paths ignore.