macOS login keychain corrupted twice in 3 days (CSSMERR_CSP_INVALID_DATA); corruption timing matches Claude Code credential writes under ~20 concurrent instances

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

Environment

  • macOS 26.5.2 (build 25F84), Apple Silicon (MacBook Air), FileVault on
  • Claude Code CLI 2.1.226 (native install, ~/.local/bin/claude)
  • Claude Desktop app hosting claude-code 2.1.227
  • Cursor IDE extension anthropic.claude-code 2.1.229
  • Auth: Claude.ai subscription (OAuth), no API key

Summary

The legacy login keychain (~/Library/Keychains/login.keychain-db) was corrupted twice within 3 days. After corruption, /login completes and prints "Login successful", but the credential write silently fails and the next command shows "Not logged in · Please run /login" (or "Failed to retrieve auth status after login" in the login UI). The keychain becomes fully unreadable and unwritable for every consumer, not just Claude Code, and cannot be repaired — only deleted and rebuilt. It re-corrupted ~36 hours after a clean rebuild under the same usage pattern.

Key evidence

1. securityd fails to decode the keychain master key — data corruption, not an auth failure:

2026-08-13 18:00:54.836 securityd [KCdb] ... decoding for makeUnlocked()
2026-08-13 18:00:54.836 securityd [security_exception] CSSM Exception: -2147415994 CSSMERR_CSP_INVALID_DATA
2026-08-13 18:00:54.836 securityd [security_exception] MacOS error: -25337
...
2026-08-14 00:50:34.634 securityd [SSkey] blob() caught exception

After this, every security find-generic-password / add-generic-password against the login keychain returns errSecAuthFailed (-25293), even for unrelated test items. Same signature on both corruption events.

2. Both corruptions coincide with a Claude Code credential write, to the second.

The com.apple.quarantine xattr on the keychain file is re-stamped on each full-file rewrite; its timestamp equals the mdat of the "Claude Code-credentials" generic-password item exactly, on both events:

| Event | Last successful credential write (mdat == quarantine ts) | First decode failure in securityd log |
|---|---|---|
| 1 | 2026-08-10 21:48:14 (local, UTC+8) | 2026-08-11 10:29:35 |
| 2 | 2026-08-13 17:53:11 | 2026-08-13 18:00:54 (~7.5 min later) |

So in both cases the last process to rewrite the keychain file before corruption was a Claude Code credential store.

3. Heavy multi-instance concurrency at corruption time.

At event 2 there were 21 independent claude main processes on the machine: 16 spawned by a single Cursor extension host (sessions opened over the previous day), plus desktop-app-hosted sessions and a terminal CLI. All share the same "Claude Code-credentials" item. The first corruption also occurred during a period of many parallel sessions. The old keychain additionally contained ~48 stale Claude Code-credentials-<hash> entries accumulated from sandboxed / custom-config-dir sessions.

Hypothesis

The legacy file keychain is rewritten as a whole file on item writes and has weak cross-process serialization. Multiple concurrent Claude Code instances refreshing/storing the shared OAuth credential can produce a torn write, leaving the master-key blob undecodable (CSSMERR_CSP_INVALID_DATA).

I'm aware the v2.1.118 release notes mention "Fixed macOS keychain race condition where concurrent MCP token refresh could overwrite freshly-refreshed OAuth tokens" and a cross-process lock fix. All components here are ≥ 2.1.226, so this scenario appears not to be covered by that fix (or the lock does not extend to credential writes from independently installed binaries: native CLI vs desktop-bundled vs Cursor-bundled, which are three separate builds sharing one keychain item).

Impact

  • Keychain is unrecoverable; user must delete login.keychain-db and rebuild, losing all stored items (app Safe Storage keys, gh/CLI tokens, etc.).
  • Recurred within 36h after rebuild, same signature.
  • Misleading UX: /login reports "Login successful" while the credential write actually failed (write result appears unchecked), which sends debugging in the wrong direction (looks like an auth/proxy issue, is actually storage).

Suggested directions

  1. Serialize keychain credential writes across all Claude Code processes (including desktop-bundled and IDE-bundled binaries) with a cross-process file lock.
  2. Reduce rewrite frequency of the shared credential item (write only on actual token change).
  3. Surface a write-verification error in /login instead of "Login successful" when the readback fails.

Happy to provide full securityd log excerpts, file metadata, or run a diagnostic build. I have kept both corrupted keychain files.

View original on GitHub ↗

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