git-ecosystem/git-credential-manager · error · InteropException

ErrorSecItemNotFound

ErrorSecItemNotFound

Error message

The item cannot be found.

What it means

InteropException thrown by SecurityFramework.ThrowIfError when the macOS Security framework returns errSecItemNotFound (-25300). It means a query (SecItemCopyMatching, SecKeychainFindGenericPassword, SecKeychainItemCopyFromPersistentReference) matched no keychain item for the given service/account/persistent reference. This is the keychain equivalent of 'record not found', not a system failure.

Solutions

  1. Verify the item exists (Keychain Access or `security find-generic-password -s <service>`) and that service/account strings match exactly.
  2. Store the item first if missing, or handle not-found as a normal 'no credential yet' case rather than an unexpected failure.
  3. Ensure you are querying the same keychain the item was created in (pass the explicit keychain ref, not null/default).
  4. If using a persistent reference, re-acquire it from a fresh SecKeychainItemCopyFromPersistentReference-capable source; recreate the item and store a new reference if it is stale.
  5. Normalize service/account strings (trim, consistent casing) before both storing and querying.

Example fix

// before: throwing away the not-found case as fatal
err = SecKeychainFindGenericPassword(null, serviceLength, service, accountLength, account, out length, out data, IntPtr.Zero);
SecurityFramework.ThrowIfError(err); // InteropException -25300 when nothing stored yet

// after: treat not-found as 'no credential stored yet'
err = SecKeychainFindGenericPassword(null, serviceLength, service, accountLength, account, out length, out data, IntPtr.Zero);
if (err == ErrorSecItemNotFound)
{
    credential = ProvisionNewCredential(); // store it via SecKeychainAddGenericPassword
}
else
{
    SecurityFramework.ThrowIfError(err);
    credential = ReadCredential(data, length);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Probe existence before reading the secret
int err = SecKeychainFindGenericPassword(null, service.Length, service, account.Length, account, out _, out IntPtr _, out IntPtr _);
bool credentialStored = err == OK; // -25300 means nothing stored yet under that service/account
if (!credentialStored) ProvisionCredential();

Type guard

static bool IsSecItemNotFound(InteropException ex) => ex.ErrorCode == -25300; // errSecItemNotFound

Try / catch

try
{
    credential = ReadCredentialFromKeychain(service, account);
}
catch (InteropException ex) when (ex.ErrorCode == -25300)
{
    // No credential stored yet — normal first-run case.
    credential = ProvisionAndStoreCredential(service, account);
}

Prevention

When it happens

Trigger: SecKeychainFindGenericPassword with a service/account string that was never stored (or stored under different casing/whitespace); SecItemCopyMatching whose query dictionary matches zero items; SecKeychainItemCopyFromPersistentReference given a stale persistent reference from an item that was deleted or from a different keychain.

Common situations: Querying a credential before the code path that stores it ever ran; the item was created in the login keychain but the app now targets a different (custom/Shared) keychain; typos or trimming differences in the service name; the user deleted the item via Keychain Access; restoring a persistent reference after a keychain reset or macOS migration.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of git-ecosystem/git-credential-manager@e8ce762cd0 (2026-09-11). Data as JSON: /api/errors/bbcdc324d1cae9d7. Report an issue: GitHub.

Appendix: source

Thrown at src/Core/Interop/MacOS/Native/SecurityFramework.cs:150

        public const int ErrorSecInteractionRequired = -25315;
        public const int ErrorSecNoSuchAttr = -25303;

        public static void ThrowIfError(int error, string defaultErrorMessage = "Unknown error.")
        {
            switch (error)
            {
                case OK:
                    return;
                case ErrorSecNoSuchKeychain:
                    throw new InteropException("The keychain does not exist.", error);
                case ErrorSecInvalidKeychain:
                    throw new InteropException("The keychain is not valid.", error);
                case ErrorSecAuthFailed:
                    throw new InteropException("Authorization/Authentication failed.", error);
                case ErrorSecDuplicateItem:
                    throw new InteropException("The item already exists.", error);
                case ErrorSecItemNotFound:
                    throw new InteropException("The item cannot be found.", error);
                case ErrorSecInteractionNotAllowed:
                    throw new InteropException("Interaction with the Security Server is not allowed.", error);
                case ErrorSecInteractionRequired:
                    throw new InteropException("User interaction is required.", error);
                case ErrorSecNoSuchAttr:
                    throw new InteropException("The attribute does not exist.", error);
                default:
                    throw new InteropException(defaultErrorMessage, error);
            }
        }
    }

    [Flags]
    public enum SessionAttributeBits
    {
        SessionIsRoot = 0x0001,
        SessionHasGraphicAccess = 0x0010,
        SessionHasTty = 0x0020,

View on GitHub (pinned to e8ce762cd0)