diff --git a/Sources/CodexBarCore/KeychainCacheStore.swift b/Sources/CodexBarCore/KeychainCacheStore.swift index e77ebbd77..a30ea3eca 100644 --- a/Sources/CodexBarCore/KeychainCacheStore.swift +++ b/Sources/CodexBarCore/KeychainCacheStore.swift @@ -46,13 +46,14 @@ public enum KeychainCacheStore { return testResult } #if os(macOS) - let query: [String: Any] = [ + var query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: self.serviceName, kSecAttrAccount as String: key.account, kSecMatchLimit as String: kSecMatchLimitOne, kSecReturnData as String: true, ] + KeychainNoUIQuery.apply(to: &query) var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) @@ -70,6 +71,12 @@ public enum KeychainCacheStore { return .found(decoded) case errSecItemNotFound: return .missing + case errSecInteractionNotAllowed: + // Keychain is temporarily locked (e.g. immediately after wake from sleep). + // The cache entry is valid — treat as missing so the caller falls through + // gracefully rather than deleting a perfectly good cache entry. + self.log.info("Keychain cache temporarily locked (\(key.account)), will retry on next access") + return .missing default: self.log.error("Keychain cache read failed (\(key.account)): \(status)") return .invalid