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

Unknown error.

Error message

Unknown error.

What it means

ThrowIfError's default case throws InteropException with the generic 'Unknown error.' message when a macOS Security.framework status code has no explicit mapping. It preserves the raw error code so the underlying Sec status can still be diagnosed.

Solutions

  1. Inspect the InteropException's raw error code and map it to the errSec constant (compare with Apple's Security framework docs).
  2. Ensure a keychain is available and unlocked in the environment (headless: create/unlock a keychain with `security create-keychain` / `security unlock-keychain`).
  3. Configure an alternative credential store (cache, plaintext file, GPG/pass) via GCM's credential.credentialStore setting.
  4. If a common errSec code recurs, extend the switch in ThrowIfError with an explicit case.

Example fix

// before
case ErrorSecNoSuchAttr:
    throw new InteropException("The attribute does not exist.", error);
default:
    throw new InteropException(defaultErrorMessage, error);
// after
case ErrorSecNoSuchAttr:
    throw new InteropException("The attribute does not exist.", error);
case ErrorSecMissingEntitlement:
    throw new InteropException("Missing keychain entitlement (check hardening/entitlements).", error);
default:
    throw new InteropException($"Unknown security error {error}.", error);
Defensive patterns

Strategy: fallback

Validate before calling

// Check a usable keychain exists before invoking the helper:
// security show-keychain-info  (fails if no default/unlocked keychain)

Type guard

static bool HasUsableKeychain() =>
    Environment.OSVersion.Platform == PlatformID.MacOSX &&
    Run("security", "show-keychain-info") == 0;

Try / catch

try { cred = keychainStore.Get(...); }
catch (InteropException ex)
{ log.Warn($"Keychain error {ex.ErrorCode}; falling back to cache store"); cred = cacheStore.Get(...); }

Prevention

When it happens

Trigger: Any Security.framework call (SecItemAdd, SecItemCopyMatching, SecItemDelete, etc.) returning an errSec status code not handled by the switch in ThrowIfError — e.g. errSecMissingEntitlement, errSecNotAvailable, or other unmapped statuses.

Common situations: Running on locked-down CI with no keychain access (errSecMissingEntitlement surfacing as unknown); keychain locked or unavailable in headless/SSH sessions; corrupt keychain; unusual macOS versions returning newer status codes.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

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

                    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)]
    public struct SecKeychainAttributeInfo
    {
        public uint Count;
        public IntPtr Tag; // uint* (SecKeychainAttrType*)

View on GitHub (pinned to e8ce762cd0)