Skip to content

fix: handle errSecInteractionNotAllowed in KeychainCacheStore to prevent cache self-destruction on wake#594

Merged
steipete merged 2 commits intosteipete:mainfrom
josepe98:fix/keychain-cache-interaction-not-allowed
Apr 18, 2026
Merged

fix: handle errSecInteractionNotAllowed in KeychainCacheStore to prevent cache self-destruction on wake#594
steipete merged 2 commits intosteipete:mainfrom
josepe98:fix/keychain-cache-interaction-not-allowed

Conversation

@josepe98
Copy link
Copy Markdown
Contributor

Problem

When the Mac wakes from sleep, the keychain is briefly locked. During this window, KeychainCacheStore.load calls SecItemCopyMatching and gets back errSecInteractionNotAllowed (-25308). This falls into the default case, returns .invalid, and the caller deletes the cache entry:

// In Repository.loadRecord
case .invalid:
    KeychainCacheStore.clear(key: ClaudeOAuthCredentialsStore.cacheKey)  // cache wiped

The cache entry was perfectly valid — it was just temporarily inaccessible. Deleting it forces a fresh read of "Claude Code-credentials" on the next access, which triggers a keychain prompt. This is why users see repeated password prompts after wake from sleep even after clicking "Always Allow".

Fix

Two small changes to KeychainCacheStore.load:

  1. Apply KeychainNoUIQuery to the load query — consistent with how other no-UI reads are performed elsewhere in the codebase, and ensures the call fails fast with errSecInteractionNotAllowed rather than blocking.

  2. Add an explicit case errSecInteractionNotAllowed that returns .missing instead of .invalid. The entry is valid but temporarily inaccessible — the caller should fall through gracefully and retry on next access, not destroy the cache.

Context

errSecInteractionNotAllowed is already handled explicitly in ClaudeOAuthCredentials.swift and KeychainAccessPreflight.swift, but was missed in KeychainCacheStore itself. The KeychainNoUIQuery helper exists precisely for this purpose but wasn't applied to cache reads.

Note: I'm a user who hit this bug (repeated prompts after wake) and traced it to this specific code path.

josepe98 and others added 2 commits April 18, 2026 21:29
…ent cache self-destruction on wake

When the keychain is temporarily locked (e.g. immediately after wake from
sleep), SecItemCopyMatching returns errSecInteractionNotAllowed (-25308).
Previously this fell into the default case, returned .invalid, and the
caller deleted the cache entry — causing every wake from sleep to require
a fresh read of "Claude Code-credentials", which triggers a keychain
prompt.

Two changes:
1. Apply KeychainNoUIQuery to the cache load query so the call never
   blocks waiting for UI interaction (consistent with how other no-UI
   reads are done elsewhere in the codebase).
2. Add an explicit case for errSecInteractionNotAllowed that returns
   .missing instead of .invalid — the entry is valid, just temporarily
   inaccessible, so it should not be deleted.
@steipete steipete force-pushed the fix/keychain-cache-interaction-not-allowed branch from a36e41b to 6d7b4aa Compare April 18, 2026 20:34
@steipete steipete merged commit aa26573 into steipete:main Apr 18, 2026
1 check passed
@steipete
Copy link
Copy Markdown
Owner

Landed via temp rebase onto main.

  • Gate: swiftformat Sources Tests; swiftlint --strict; pnpm check; swift build; swift test -q; ./Scripts/compile_and_run.sh
  • Land commit: 6d7b4aa
  • Merge commit: aa26573

Thanks @josepe98!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants