anthropics / anthropics/claude-code
[BUG] macOS: claude auth login reports success but CLI stays unauthenticated - hasAvailableSubscription false on an active Stripe subscription, and no credential is persisted
- Dominant language
- Python
- Stars
- 145k
- Forks
- 23.1k
- PR merge metrics
- PR metrics pending
Description
## Bug Description
On macOS, `claude auth login` completes and prints `Login successful.`, but the CLI remains entirely unauthenticated. `claude auth status` reports `loggedIn: false` / `authMethod: "none"`, and any `claude -p` invocation fails with `Failed to authenticate: OAuth session expired and could not be refreshed`. `~/.claude.json` shows `hasAvailableSubscription: false` against an active `stripe_subscription`.
This is **not** the same as #93313 or #33119, where authentication succeeds and only Remote Control is gated. Here the CLI is unusable: `loggedIn` never becomes true. It matches #51971 (closed, no fix, no maintainer response), including that reporter's observation that the trigger appears to be **an active subscription and a Console organization on the same email address**.
## Reproduction, from a fully clean credential state
To rule out every client-side cause, all stored credentials were removed first:
```
security delete-generic-password -s "Claude Code-credentials" # deleted
rm ~/.claude/.credentials.json # absent
claude auth status -> loggedIn: false, authMethod: "none"
```
Then:
```
claude auth login
Opening browser to sign in...
Paste code here if prompted > Login successful.
claude auth status
{ "loggedIn": false, "authMethod": "none", "apiProvider": "firstParty" }
```
After that successful login, **nothing was persisted**: no `Claude Code-credentials` keychain item was created, and no `~/.claude/.credentials.json` was written.
The OAuth exchange itself clearly succeeds -- `~/.claude.json` `oauthAccount` is populated with the correct account UUID, email, organization UUID, `organizationRole: owner`, and `billingType: stripe_subscription` -- yet:
```
claudeMaxTier = not_max
hasAvailableMaxSubscription = false
hasAvailableSubscription = false
```
The requested scope set includes `user:profile`:
```
scope=org:create_api_key user:profile user:inference
user:sessions:claude_code user:mcp_servers user:file_upload
```
## Secondary finding: a login can write a credential with empty tokens
Before the clean-state test, an earlier login on the same machine wrote a keychain record that was structurally valid but had its secrets blanked:
```json
{
"claudeAiOauth": {
"accessToken": "", // empty string
"refreshToken": "", // empty string
"expiresAt": 0,
"scopes": ["user:file_upload","user:inference","user:mcp_servers",
"user:profile","user:sessions:claude_code"],
"subscriptionType": "...",
"rateLimitTier": "..."
},
"organizationUuid": "..."
}
```
Scopes, subscription type and organization survived; both tokens were empty and `expiresAt` was 0. That record fully explains the runtime error -- `expiresAt: 0` reads as expired, and there is no refresh token to refresh with -- but it should not have been written in that state.
The same login path also left `~/.claude/.credentials.json` **malformed** (truncated by one closing brace, so nothing could parse it) and dropped two `mcpOAuth` entries that had been present, shrinking the file from 1147 to 556 bytes. Both look like the same writer failing to persist string values.
## Also observed: unbounded keychain item accumulation
The login keychain holds **89** `Claude Code-credentials-<8 hex>` items, accumulating at almost exactly one per day (73 of 89 share the same hour of day), spanning 2026-04-25 to present. This appears to be #91158. Mentioned only because it produced a large amount of noise while diagnosing the above; it is not the cause.
## `claude doctor` output is misleading here
```
- macOS Keychain is not writable. Console login will fail to save your API key.
Fix: Run: security unlock-keychain ~/Library/Keychains/login.keychain-db
```
The keychain is **not** the problem. It reports `no-timeout` (unlocked), and generic-password items can be created, updated in place and deleted successfully via `/usr/bin/security` on this machine -- verified with a throwaway item. The ACLs on existing `Claude Code-credentials*` items are also consistent with each other (entry granting `decrypt` lists `/usr/bin/security`, partition `apple-tool:`), so there is no ACL divergence between working and non-working entries. This warning sends people toward `unlock-keychain`, which cannot help, and away from the entitlement flag that is actually stuck.
## Expected
`claude auth login` succeeding should leave the CLI authenticated, or fail with an actionable message naming the entitlement problem.
## Actual
Login succeeds server-side, `hasAvailableSubscription` remains `false`, no credential is persisted, and the CLI stays unusable. No client-side remedy reaches it -- #51971's reporter additionally tried reinstalling Node and Claude Code, deleting `~/.claude.json`, clearing browser cookies, deleting the Console organization, and hand-patching `hasAvailableSubscription` to `true`, all without effect.
## Environment
- Claude Code **2.1.206** (homebrew, `darwin-arm64`)
- macOS, zsh
- Subscription active, billed via Stripe, account is `organizationRole: owner` of a Console organization on the same email address
Account UUID, organization UUID and email are deliberately omitted from this public issue and can be supplied privately for the manual entitlement check this appears to need.
## Related
- #51971 -- same symptom, closed without fix or maintainer response
- #93313 -- same stale flag, milder symptom (auth works, Remote Control gated)
- #33119 -- closed, stale
- #91158 -- keychain item accumulation
Contributor guide
No contributing guide indexed for this repository
Research direction
Reproduce from a clean credential state using the reported `claude auth login` and `claude auth status` commands, then inspect `~/.claude.json`, `~/.claude/.credentials.json`, and the macOS Keychain entries. Done means a successful login persists valid credentials and `loggedIn` becomes true, or the command reports an actionable entitlement error instead of claiming success.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- macos
- Domain
- authentication, cli
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100