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

ErrorSecInteractionNotAllowed

ErrorSecInteractionNotAllowed

Error message

Interaction with the Security Server is not allowed.

What it means

InteropException thrown by SecurityFramework.ThrowIfError when the macOS Security framework returns errSecInteractionNotAllowed (-25308). It means the requested keychain operation needed to talk to the Security Server (prompt the user, unlock the keychain) but the current session/process context forbids user interaction. The library cannot complete the operation without a UI, so it throws instead of hanging.

Solutions

  1. Unlock the keychain non-interactively first: `security unlock-keychain -p <password> <keychain>` or SecKeychainUnlock with the password.
  2. Run the process inside a logged-in GUI session (e.g. a LaunchAgent with LimitLoadToSessionType=Aqua) rather than a system daemon or SSH session.
  3. Partition the keychain for CI (`security set-key-partition-list -S apple-tool:,apple: -k <password> <keychain>`) so items are readable without prompts.
  4. For items requiring biometric/password auth (Touch ID / kSecAccessControl), drop that requirement or gate the operation on a UI-capable context.
  5. Verify the process user matches the keychain owner; root or other users cannot be granted interactive access to a login keychain.

Example fix

// before: CI daemon reads a locked login keychain and throws
err = SecKeychainFindGenericPassword(null, serviceLength, service, accountLength, account, out length, out data, IntPtr.Zero);
SecurityFramework.ThrowIfError(err); // -25308, no UI available to unlock

// after: pre-unlock the keychain non-interactively in CI setup
// security unlock-keychain -p "$KEYCHAIN_PASSWORD" login.keychain
// security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" login.keychain
err = SecKeychainFindGenericPassword(null, serviceLength, service, accountLength, account, out length, out data, IntPtr.Zero);
SecurityFramework.ThrowIfError(err);
Defensive patterns

Strategy: retry

Validate before calling

// Verify the process can access the keychain without UI before touching it
var (exitCode, _) = Run("security", "show-keychain-info login.keychain");
bool keychainAccessibleNonInteractively = exitCode == 0;
if (!keychainAccessibleNonInteractively) throw new InvalidOperationException("Unlock the keychain before running: security unlock-keychain -p <pwd> login.keychain");

Type guard

static bool IsSecInteractionNotAllowed(InteropException ex) => ex.ErrorCode == -25308; // errSecInteractionNotAllowed

Try / catch

try
{
    credential = ReadCredentialFromKeychain(service, account);
}
catch (InteropException ex) when (ex.ErrorCode == -25308)
{
    // No UI available (SSH/daemon/CI): unlock non-interactively, then retry once.
    UnlockKeychainNonInteractive(keychainPassword);
    credential = ReadCredentialFromKeychain(service, account);
}

Prevention

When it happens

Trigger: Keychain reads (SecKeychainFindGenericPassword, SecItemCopyMatching) on a locked keychain from a process with no UI access: SSH session, launchd daemon, CI runner, or app running as another user; SecItemCopyMatching with kSecUseAuthenticationUIDisallow while the item requires user presence; background service attempting to access items whose ACL requires confirmation.

Common situations: CI/CD pipelines unlocking the login keychain non-interactively; macOS background daemons/agents started before login window; SSH remote builds touching keychain-backed settings; apps sandboxed or running as root where Security Server denies UI; Headless macOS VMs in test farms.

Related errors


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

Appendix: source

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

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

View on GitHub (pinned to e8ce762cd0)