All usage widgets show `[No credentials]` on macOS when the login keychain has multiple `Claude Code-credentials` items (different accounts)

Author: vishnujayvelCreated Jul 20, 2026Updated Sep 1, 2026

Summary

On macOS, every usage widget (Session Usage, Weekly Usage, etc.) renders [No credentials] on some machines even though a valid Claude Code OAuth token is present in the keychain. The token is simply stored under a keychain item that ccstatusline's lookup doesn't select.

Root cause: the login keychain can contain more than one generic-password item under the service name Claude Code-credentials, differing only by account. ccstatusline reads with the service name only:

security find-generic-password -s "Claude Code-credentials" -w

With multiple items sharing that service, security returns whichever it matches first — which may be an item that has no claudeAiOauth (so parseUsageAccessToken returns null) — and ccstatusline never tries the other account.

How this happens

Claude Code itself always reads/writes the credential with an explicit account (the OS username):

security ... generic-password -a <os-username> -s "Claude Code-credentials" ...

On an affected machine, the keychain ends up with (at least) two items:

service account contents
Claude Code-credentials <os-username> (e.g. vishnu) { mcpOAuth, claudeAiOauth } ← the real token
Claude Code-credentials unknown { mcpOAuth } only ← no usage token

The unknown-account item appears to be written by an MCP-OAuth code path that didn't resolve a username. security -s with no -a can return that one first → [No credentials], deterministically wrong on that machine.

(There may also be many Claude Code-credentials-<hex> suffixed services — these are per-server MCP OAuth tokens and do not contain claudeAiOauth; the existing dump-keychain fallback that scans suffixed services can't help here because it excludes the plain service name and none of the suffixed items hold the account token.)

Reproduce

Structure-only probe (does not print token values):

bash
# Enumerate accounts under the plain service:
security dump-keychain 2>/dev/null \
  | grep -B2 '"svce"<blob>="Claude Code-credentials"$' \
  | grep '"acct"' | sort -u
# On an affected machine you'll see two accounts, e.g. "unknown" and "<your-username>".

# Which item has the token? (structure only — prints true/false, never the token)
has_token() { python3 -c 'import sys,json; print("has claudeAiOauth:", "claudeAiOauth" in (json.load(sys.stdin) or {}))'; }
security find-generic-password -s "Claude Code-credentials" -w | has_token                    # -> false (service-only match: MCP-only)
security find-generic-password -s "Claude Code-credentials" -a "$(id -un)" -w | has_token      # -> true  (OS-username account)

Proposed fix

Make the keychain read account-aware, mirroring how Claude Code itself accesses the item:

  1. Keep the current service-only read (works on single-item keychains).
  2. If it yields no claudeAiOauth, read -s "Claude Code-credentials" -a <os.userInfo().username>.
  3. Existing suffixed-service and .credentials.json fallbacks unchanged.

This is minimal, matches Claude Code's own access pattern (no fragile reverse-engineering), and is safe on machines with a single credential item (the new read only fires when the service-only read misses). It resolves the OS-username-account case observed here; it deliberately doesn't guess non-username account strings.

I have this fix implemented with unit tests and can open a PR if you'd like — flagging first since #219 and #266 also touch per-account/auth-scoping and I don't want to collide.

Environment

  • macOS (Darwin arm64), Claude Code (native install)
  • ccstatusline latest main
  • Distinct from #236 (that was token-in-file-instead-of-keychain; here the token is in the keychain, just under a non-default account).