anthropics / anthropics/claude-code

CLAUDE_CONFIG_DIR set to the default path ($HOME/.claude) selects a different Keychain credential entry than leaving it unset, with no diagnosis

Open
#92,252 0 comments 0 reactions 0 assignees View on GitHub
area:auth bug has repro platform:macos
Dominant language
Python
Stars
145k
Forks
23.1k
PR merge metrics
PR metrics pending

Description

### What's Wrong?

Per the docs (Authentication → Credential management), setting `CLAUDE_CONFIG_DIR` keys the macOS Keychain entry to that directory, "so a session with a different `CLAUDE_CONFIG_DIR` reads a different entry." That's expected when the directory actually differs. But here the directory is byte-identical to the default — only whether the variable is textually present differs — and the result is a full loss of login, with nothing in `auth status` or the "Not logged in · Please run /login" message saying which credential store was consulted.

Any script or automation that forwards `CLAUDE_CONFIG_DIR` unconditionally (e.g. to pin a relocated install's root, or because it defensively re-sets `process.env` values) silently breaks auth for that child process the moment the parent never had the variable set in the first place.

### Steps to Reproduce

```bash
claude auth status
# → {"loggedIn": true, "authMethod": "claude.ai", ...}

CLAUDE_CONFIG_DIR="$HOME/.claude" claude auth status
# → {"loggedIn": false, "authMethod": "none"}
```

Variants that isolate it to the variable's literal presence/value, not path resolution:

```bash
CLAUDE_CONFIG_DIR="$HOME/.claude/" claude auth status # trailing slash → still false
CLAUDE_SECURESTORAGE_CONFIG_DIR="$HOME/.claude" claude auth status # → still false
```

(An unexpanded `CLAUDE_CONFIG_DIR='~/.claude'` also fails, but that's a genuinely different directory — `auth status` reports `projectsDirectory: "~/.claude/projects"`, literal tilde, unresolved — so it isn't a same-directory data point and is omitted here.)

Workaround, pinning the default credential store explicitly — restores login but not identity reporting:

```bash
CLAUDE_CONFIG_DIR="$HOME/.claude" CLAUDE_SECURESTORAGE_CONFIG_DIR="" claude auth status
# → {"loggedIn": true, "authMethod": "claude.ai", "email": null, "orgId": null, "orgName": null, "subscriptionType": "max"}
```

### Environment

- `claude --version`: 2.1.261 (npm install under Homebrew's node prefix)
- macOS 26.6.2 (arm64)
- OAuth subscription login (Claude.ai, Max plan); `~/.claude/.credentials.json` does not exist — Keychain-only credential store
- Regression status: unknown, not tested on earlier versions (per #79223, the CLI has recognized `CLAUDE_SECURESTORAGE_CONFIG_DIR` since roughly 2.1.144, which is the related-but-separate mechanism this report's `CLAUDE_CONFIG_DIR` keying appears to route through — not a claim about when `CLAUDE_CONFIG_DIR`'s own keying began)

### What Should Happen?

Primarily: make this diagnosable. `claude auth status` and the `--print` "Not logged in" path should report which credential store/Keychain entry was consulted (and that an explicit `CLAUDE_CONFIG_DIR` selects a different one than unset), instead of an undifferentiated "Not logged in · Please run /login". This should also be documented alongside the existing `CLAUDE_CONFIG_DIR` docs, not only inferable from `CLAUDE_SECURESTORAGE_CONFIG_DIR` behavior.

Alternative (one clause): resolve an explicit `CLAUDE_CONFIG_DIR` that is byte-identical to the default to the same credential entry as leaving it unset. (This has a migration wrinkle — anyone who already logged in under the explicit path has a credential in the other slot — so the diagnosability fix above is the safer primary ask.)

### Related

- #87447 — "Repro C" in that issue is this same unset-vs-explicit-default discrepancy (seen there as `loggedIn:true` with null identity on Windows' plaintext credential file; here as a clean `loggedIn:false` on macOS Keychain). Buried as the third of three repros in a broader issue about a different primary complaint.
- #79223 — documents `CLAUDE_SECURESTORAGE_CONFIG_DIR` behavior in detail, including "setting the variable to your default config directory's absolute path selects a different, empty credential store, which presents as a lost login" and "an empty value pins the default credential store." This issue's repro matches that description exactly; #79223 is a docs request for the sibling variable, not a report against `CLAUDE_CONFIG_DIR`'s own behavior or the error message.
- #88601 — describes per-config-dir credential isolation as current behavior (different directories get different Keychain items, `Claude Code-credentials-`); closed by its own author, not a maintainer statement of intent. Not in conflict with this report, which is about explicit-vs-unset for the *same* directory.
- #84275 / #90527 — a different, adjacent defect (hash-suffixed Keychain items accumulating daily, never cleaned up), not this one.

### Additional context

Found this while debugging a background automation that unconditionally forwarded `CLAUDE_CONFIG_DIR` to a spawned child process — even when the parent never had the variable set, the forwarding code still injected the resolved-to-default path, reproducing this exactly. Only forwarding the variable when the parent actually had it set resolved it.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by reproducing the unset and explicit-default CLAUDE_CONFIG_DIR commands through the auth status and --print entry points. Read the Authentication → Credential management documentation and related issue #79223, then trace how the credential store is selected. Done means auth status and --print identify the consulted store, with documentation updated, or equivalent paths consistently select one credential entry.

Written by the indexing model from the issue text.

Assessment

Tech stack
macos, python, shell
Domain
authentication, cli, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.