git-ecosystem/git-credential-manager · error · InteropException
ErrorSecAuthFailed
ErrorSecAuthFailed
Error message
Authorization/Authentication failed.
What it means
InteropException thrown by SecurityFramework.ThrowIfError when the macOS Security framework returns errSecAuthFailed (-25293). It means the keychain rejected an authorization/authentication attempt, e.g. the user entered a wrong password, denied access, or the caller lacks rights to unlock or read the keychain item. The library surfaces raw Security.framework result codes as exceptions when wrapping P/Invoke calls.
Solutions
- Unlock the keychain first (open Keychain Access and unlock it, or call SecKeychainUnlock with the correct password).
- Reset the item's ACL: delete the existing keychain item and re-create it from this app so the access control list trusts the current binary.
- Run inside a logged-in GUI session instead of SSH/launchd headless context, or provision the keychain for CI (security unlock-keychain with correct password).
- Verify the app's code signature matches the one that originally stored the item; re-store the password after re-signing.
- Check the password/credentials being passed are correct; repeated failures on unlock also surface as errSecAuthFailed.
Example fix
// before: assuming the login keychain is always unlocked int err = SecKeychainFindGenericPassword(null, serviceLength, service, accountLength, account, out length, out data, IntPtr.Zero); SecurityFramework.ThrowIfError(err); // throws InteropException -25293 when keychain is locked // after: unlock the keychain explicitly before querying byte[] password = Encoding.UTF8.GetBytes(userPassword); err = SecKeychainUnlock(keychainRef, (uint)password.Length, password, false); SecurityFramework.ThrowIfError(err); err = SecKeychainFindGenericPassword(null, serviceLength, service, accountLength, account, out length, out data, IntPtr.Zero); SecurityFramework.ThrowIfError(err);
Defensive patterns
Strategy: try-catch
Validate before calling
// Check keychain lock state before calling (macOS CLI or via SecKeychainGetStatus) // security show-keychain-info login.keychain # fails or prompts if locked var status = SecKeychainGetStatus(keychainRef, out SecKeychainStatus flags); bool keychainUnlocked = (flags & SecKeychainStatus.Unlocked) != 0; if (!keychainUnlocked) UnlockKeychainWithStoredPassword();
Type guard
static bool IsSecAuthFailed(InteropException ex) => ex.ErrorCode == -25293; // errSecAuthFailed
Try / catch
try
{
ReadCredentialFromKeychain(service, account);
}
catch (InteropException ex) when (ex.ErrorCode == -25293)
{
// Wrong password / access denied / keychain locked.
// Re-prompt the user, re-store the credential, or unlock the keychain.
logger.LogWarning("Keychain auth failed for {Service}", service);
await ReauthenticateAndReStoreAsync();
} Prevention
- Unlock the keychain (SecKeychainUnlock or `security unlock-keychain`) before automated keychain access.
- Run keychain-touching code in a logged-in GUI session; avoid SSH/daemons for interactive items.
- Re-store credentials after re-signing or rebuilding the app so its ACL matches the binary signature.
- Never pass guessed passwords to unlock calls; fetch them from a trusted bootstrap source.
- Catch InteropException by its ErrorCode rather than message text when differentiating keychain failures.
When it happens
Trigger: Calling keychain P/Invokes such as SecKeychainFindGenericPassword, SecKeychainItemCopyContent, SecItemCopyMatching, or SecKeychainAddGenericPassword (via ThrowIfError) when the keychain is locked and the supplied/unlock password is wrong, the user clicked Deny in the access prompt, or the ACL does not grant the calling app access to the item.
Common situations: Running in an SSH/headless session where no GUI user can answer the keychain unlock prompt; a keychain locked with a password different from the login password; the app was re-signed or rebuilt so its code signature no longer matches the item ACL; CI machines with a locked login keychain; wrong credentials passed programmatically to SecKeychainUnlock or item creation.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- ErrorSecNoSuchKeychain
- ErrorSecInvalidKeychain
- ErrorSecDuplicateItem
- ErrorSecItemNotFound
- ErrorSecInteractionNotAllowed
AI-assisted analysis of git-ecosystem/git-credential-manager@e8ce762cd0 (2026-09-11).
Data as JSON: /api/errors/85d0fc8a5bf2fab9.
Report an issue: GitHub.
Appendix: source
Thrown at src/Core/Interop/MacOS/Native/SecurityFramework.cs:146
public const int ErrorSecAuthFailed = -25293;
public const int ErrorSecDuplicateItem = -25299;
public const int ErrorSecItemNotFound = -25300;
public const int ErrorSecInteractionNotAllowed = -25308;
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 SessionAttributeBitsView on GitHub (pinned to e8ce762cd0)