Getting errSecItemNotFound from Keychain although the item is present

I am maintaining an enterprise app where we store access tokens in the Keychain after a successful login.


Whenever an API is triggered, the app retrieves the corresponding token from the Keychain and attaches it to the authentication header of the API request. Additionally, the app logs various events—including error logs—through one of the analytics managers.


Over the past few months, we have been noticing a significant number of logs related to the TOKEN_MISSING error. Upon further investigation and after adding more detailed logs, we discovered that while attempting to fetch tokens from the Keychain, we are frequently encountering the errSecItemNotFound error—even though the Keychain hasn't been reset and the item should be present.


Anyone has any idea what's going wrong here?

Posted on Jun 11, 2025 10:46 PM

Reply

Similar questions

1 reply

Jun 11, 2025 11:50 PM in response to sohamb1390

Adding to the above:


Since the app can trigger API calls in the background, I updated the Keychain access control to kSecAttrAccessibleAfterFirstUnlock. After making this change, the number of TOKEN_MISSING incidents has significantly decreased over the past week. However, I can still occasionally see errSecItemNotFound errors in the logs.


Pasting my code for saving & retrieving data from the keychain:


public enum KeychainError: Error {
        case noPassword
        case unexpectedPasswordData
        case unexpectedItemData
        case unhandledError(status: OSStatus)
    }
public let service: String = "AppUser"

public func readItem(from account: String, shouldAccessFromFirstUnlock: Bool = false) throws -> String {
        /*
         Build a query to find the item that matches the service, account and
         access group.
         */
        var query = KeychainPasswordItem.keychainQuery(withService: service, account: account, accessGroup: accessGroup, shouldAccessFromFirstUnlock: shouldAccessFromFirstUnlock)
        query[kSecMatchLimit as String] = kSecMatchLimitOne
        query[kSecReturnAttributes as String] = kCFBooleanTrue
        query[kSecReturnData as String] = kCFBooleanTrue
        
        // Try to fetch the existing keychain item that matches the query.
        var queryResult: AnyObject?
        let status = withUnsafeMutablePointer(to: &queryResult) {
            SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
        }
        
        // Check the return status and throw an error if appropriate.
        guard status != errSecItemNotFound else { throw KeychainError.noPassword }
        guard status == noErr else { throw KeychainError.unhandledError(status: status) }
        
        // Parse the password string from the query result.
        guard let existingItem = queryResult as? [String: AnyObject],
              let passwordData = existingItem[kSecValueData as String] as? Data,
              let password = String(data: passwordData, encoding: String.Encoding.utf8)
        else {
            throw KeychainError.unexpectedPasswordData
        }
        
        return password
    }


public func saveItem(_ item: String, account: String, shouldAccessFromFirstUnlock: Bool = false) throws {
        // Encode the password into an Data object.
        guard let encodedPassword = item.data(using: String.Encoding.utf8) else { return }
        
        do {
            // Check for an existing item in the keychain.
            try _ = readItem(from: account, shouldAccessFromFirstUnlock: shouldAccessFromFirstUnlock)
            
            // Update the existing item with the new password.
            var attributesToUpdate = [String: AnyObject]()
            attributesToUpdate[kSecValueData as String] = encodedPassword as AnyObject?
            
            let query = KeychainPasswordItem.keychainQuery(withService: service, account: account, accessGroup: accessGroup, shouldAccessFromFirstUnlock: shouldAccessFromFirstUnlock)
            let status = SecItemUpdate(query as CFDictionary, attributesToUpdate as CFDictionary)
            
            // Throw an error if an unexpected status was returned.
            guard status == noErr else { throw KeychainError.unhandledError(status: status) }
            
        } catch KeychainError.noPassword {
            /*
             No password was found in the keychain. Create a dictionary to save
             as a new keychain item.
             */
            var newItem = KeychainPasswordItem.keychainQuery(withService: service, account: account, accessGroup: accessGroup, shouldAccessFromFirstUnlock: shouldAccessFromFirstUnlock)
            newItem[kSecValueData as String] = encodedPassword as AnyObject?
            
            // Add a the new item to the keychain.
            let status = SecItemAdd(newItem as CFDictionary, nil)
            
            // Throw an error if an unexpected status was returned.
            guard status == noErr else { throw KeychainError.unhandledError(status: status) }
        }
    }

private static func keychainQuery(withService service: String, account: String? = nil, accessGroup: String? = nil, shouldAccessFromFirstUnlock: Bool = false) -> [String: AnyObject] {
        var query = [String: AnyObject]()
        query[kSecClass as String] = kSecClassGenericPassword
        query[kSecAttrService as String] = service as AnyObject?
        
        if shouldAccessFromFirstUnlock {
            query[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
        }
        
        if let account = account {
            query[kSecAttrAccount as String] = account as AnyObject?
        }
        
        if let accessGroup = accessGroup {
            query[kSecAttrAccessGroup as String] = accessGroup as AnyObject?
        }
        
        return query
    }


This thread has been closed by the system or the community team. You may vote for any posts you find helpful, or search the Community for additional answers.

Getting errSecItemNotFound from Keychain although the item is present

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple Account.