{"record":{"id":"8dc890ba5697f618","repo":"git-ecosystem/git-credential-manager","slug":"errorsecduplicateitem","errorCode":"ErrorSecDuplicateItem","errorMessage":"The item already exists.","messagePattern":"The item already exists\\.","errorType":"error_code","errorClass":"InteropException","httpStatus":null,"severity":"error","filePath":"src/Core/Interop/MacOS/Native/SecurityFramework.cs","lineNumber":148,"sourceCode":"        public const int ErrorSecItemNotFound = -25300;\n        public const int ErrorSecInteractionNotAllowed = -25308;\n        public const int ErrorSecInteractionRequired = -25315;\n        public const int ErrorSecNoSuchAttr = -25303;\n\n        public static void ThrowIfError(int error, string defaultErrorMessage = \"Unknown error.\")\n        {\n            switch (error)\n            {\n                case OK:\n                    return;\n                case ErrorSecNoSuchKeychain:\n                    throw new InteropException(\"The keychain does not exist.\", error);\n                case ErrorSecInvalidKeychain:\n                    throw new InteropException(\"The keychain is not valid.\", error);\n                case ErrorSecAuthFailed:\n                    throw new InteropException(\"Authorization/Authentication failed.\", error);\n                case ErrorSecDuplicateItem:\n                    throw new InteropException(\"The item already exists.\", error);\n                case ErrorSecItemNotFound:\n                    throw new InteropException(\"The item cannot be found.\", error);\n                case ErrorSecInteractionNotAllowed:\n                    throw new InteropException(\"Interaction with the Security Server is not allowed.\", error);\n                case ErrorSecInteractionRequired:\n                    throw new InteropException(\"User interaction is required.\", error);\n                case ErrorSecNoSuchAttr:\n                    throw new InteropException(\"The attribute does not exist.\", error);\n                default:\n                    throw new InteropException(defaultErrorMessage, error);\n            }\n        }\n    }\n\n    [Flags]\n    public enum SessionAttributeBits\n    {\n        SessionIsRoot = 0x0001,","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/git-ecosystem/git-credential-manager/blob/e8ce762cd04b4100ae637b5fbf39ef9d0a96561e/src/Core/Interop/MacOS/Native/SecurityFramework.cs#L130-L166","documentation":"InteropException thrown by SecurityFramework.ThrowIfError when the macOS Security framework returns errSecDuplicateItem (-25299). It means an add/create operation (e.g. SecKeychainAddGenericPassword or SecKeychainItemCreateFromContent) found an item with the same primary attributes (service+account) already in the keychain. The library refuses to silently overwrite the existing secret.","triggerScenarios":"Calling SecKeychainAddGenericPassword or SecKeychainAddInternetPassword for a (service, account) pair that already has an entry in the target keychain; re-running an initialization routine that stores a credential unconditionally without checking for an existing item.","commonSituations":"Idempotency bugs in setup/first-run code that stores a token on every launch; running the same provisioning script twice; migrating items between keychains where the destination already holds the entry; a test suite storing the same fixture credential repeatedly.","solutions":["Check existence first with SecKeychainFindGenericPassword; if found, update the item's data (SecKeychainItemModifyAttributesAndData) instead of adding.","Delete the existing item (SecKeychainItemDelete) before adding, only if overwriting is intentional.","Wrap the add call in a catch of InteropException with error -25299 and treat it as 'already stored'.","Use a unique service or account string when you genuinely need multiple distinct items."],"exampleFix":"// before: unconditional add throws on second run\nerr = SecKeychainAddGenericPassword(null, serviceLength, service, accountLength, account, (uint)password.Length, password, out itemRef);\nSecurityFramework.ThrowIfError(err);\n\n// after: look up first, modify if present, add only if missing\nerr = SecKeychainFindGenericPassword(null, serviceLength, service, accountLength, account, out _, out IntPtr data, out itemRef);\nif (err == ErrorSecItemNotFound)\n{\n    err = SecKeychainAddGenericPassword(null, serviceLength, service, accountLength, account, (uint)password.Length, password, out itemRef);\n}\nelse\n{\n    err = SecKeychainItemModifyAttributesAndData(itemRef, IntPtr.Zero, (uint)password.Length, password);\n}\nSecurityFramework.ThrowIfError(err);","handlingStrategy":"try-catch","validationCode":"// Check for an existing item before adding\nint err = SecKeychainFindGenericPassword(null, service.Length, service, account.Length, account, out _, out IntPtr _, out IntPtr _);\nbool itemExists = err != ErrorSecItemNotFound && err == OK; // OK => exists; -25299 otherwise add throws","typeGuard":"static bool IsSecDuplicateItem(InteropException ex) => ex.ErrorCode == -25299; // errSecDuplicateItem","tryCatchPattern":"try\n{\n    AddCredentialToKeychain(service, account, secret);\n}\ncatch (InteropException ex) when (ex.ErrorCode == -25299)\n{\n    // Item with same service/account already present.\n    UpdateExistingCredential(service, account, secret); // SecKeychainItemModifyAttributesAndData\n}","preventionTips":["Always find-before-add: call SecKeychainFindGenericPassword and update instead of adding when the item exists.","Make credential-storage routines idempotent so re-running setup scripts doesn't duplicate items.","Use a stable, unique (service, account) pair; avoid generating names with timestamps unless duplicates are intended.","In tests, use a dedicated throwaway keychain and clear it between runs."],"tags":["macos","keychain","security-framework","duplicate","interop"],"backgroundTag":"file-already-exists","analyzedSha":"e8ce762cd04b4100ae637b5fbf39ef9d0a96561e","analyzedAt":"2026-09-11T17:15:08.753Z","contentChangedAt":"2026-09-11T17:15:08.753Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}