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

ErrorSecInteractionRequired

ErrorSecInteractionRequired

Error message

User interaction is required.

What it means

InteropException thrown by SecurityFramework.ThrowIfError when the macOS Security framework returns errSecInteractionRequired (-25315). It means the operation can only succeed if the user is prompted (e.g. to unlock the keychain, authorize access, or satisfy an authentication context), but the call was made in a way that no prompt was shown. Unlike errSecInteractionNotAllowed, the request itself is valid; a UI round-trip is required to proceed.

Solutions

  1. Allow Security Server UI for the call (remove kSecUseAuthenticationUIDisallow; use Allow or Failover so the prompt appears).
  2. Prompt the user deliberately: run the operation from a foreground, UI-capable context after informing the user why the keychain prompt appears.
  3. If user presence is not actually required, recreate the item without kSecAccessControlUserPresence/biometry flags so reads succeed non-interactively.
  4. Retry the operation after the user completes authentication (e.g. re-call after the Touch ID / unlock dialog succeeds).
  5. For background/daemons, use items without user-presence ACL, or cache an unlocked session via SecKeychainUnlock with stored credentials.

Example fix

// before: silent query of a user-presence-protected item throws
var query = new SecQueryDictionary(service).AsDictionary();
err = SecItemCopyMatching(query, out result);
SecurityFramework.ThrowIfError(err); // -25315: needs a user prompt

// after: perform the call where UI is allowed and retry once the user authenticates
query[kSecUseAuthenticationUI] = kSecUseAuthenticationUIAllow;
err = SecItemCopyMatching(query, out result);
if (err == ErrorSecInteractionRequired)
{
    ShowExplainUnlockDialog(); // user completes Touch ID / password prompt
    err = SecItemCopyMatching(query, out result); // retry
}
SecurityFramework.ThrowIfError(err);
Defensive patterns

Strategy: retry

Validate before calling

// Detect items that require user presence before attempting non-interactive access
bool requiresUserPresence = query.ContainsKey(kSecAttrAccessControl);
if (requiresUserPresence && runningHeadless)
{
    throw new InvalidOperationException("Item requires user interaction; run in a UI session or store the item without kSecAccessControl.");
}

Type guard

static bool IsSecInteractionRequired(InteropException ex) => ex.ErrorCode == -25315; // errSecInteractionRequired

Try / catch

try
{
    credential = ReadProtectedCredential(query);
}
catch (InteropException ex) when (ex.ErrorCode == -25315)
{
    // A user prompt is mandatory: surface the request to the UI layer and retry once.
    await uiService.ShowAuthenticationPromptAsync();
    credential = ReadProtectedCredential(query); // single retry after user authenticated
}

Prevention

When it happens

Trigger: SecItemCopyMatching/SecKeychainFindGenericPassword on an item whose ACL or kSecAccessControl policy requires user consent (Touch ID, device passcode, re-authentication) without kSecUseAuthenticationUI set appropriately; first access to a locked keychain item where authorization UI was suppressed; operations needing authorization rights that must be acquired interactively.

Common situations: Touch ID / Apple Watch-protected items accessed from a context where the prompt cannot display; apps calling keychain APIs during app startup before a window is available; automated tests hitting items configured with user-presence requirement; access-control items queried with UI explicitly disabled then failing because interaction was actually mandatory.

Related errors


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

Appendix: source

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

        {
            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,
        SessionIsRemote = 0x1000,
    }

    [StructLayout(LayoutKind.Sequential)]

View on GitHub (pinned to e8ce762cd0)