ComposioHQ/composio · error · KeyringError
security add-generic-password: delete-then-add produced dupl
Error message
security add-generic-password: delete-then-add produced duplicate. The existing item may have an ACL that blocks deletion — run `security delete-generic-password -s com.composio.cli -a default` manually.
What it means
The store implements upsert as delete-then-add via the `security` CLI. If the add exits with the duplicate-item status (EXIT_DUPLICATE_ITEM), the preceding delete silently failed — typically because the existing keychain item's ACL denies modification — so the new secret could not be written. The error is a PlatformFailure KeyringError with recovery instructions embedded.
Source
Thrown at ts/packages/cli-keyring/src/stores/macos-security-subprocess.ts:176
service,
'-l',
label,
'-w',
encoded,
...domainArgs,
];
let result: SpawnResult;
try {
result = await runCommand({ command: SECURITY_BIN, args });
} catch (err) {
throw new KeyringError({ kind: 'NoStorageAccess', cause: err });
}
if (result.code === 0) return;
if (result.code === EXIT_DUPLICATE_ITEM) {
throw new KeyringError({
kind: 'PlatformFailure',
cause: new Error(
'security add-generic-password: delete-then-add produced duplicate. ' +
'The existing item may have an ACL that blocks deletion — ' +
'run `security delete-generic-password -s com.composio.cli -a default` manually.'
),
});
}
throw classifyExitCode(result, 'add-generic-password');
}
async getSecret(service: string, user: string, modifiers: EntryModifiers): Promise<Uint8Array> {
validateSpecifier(service, user);
const domainArgs = domainFlag(modifiers.keychain);
// -w prints the password to stdout and nothing else. Without -w,
// `security` dumps the item's attributes in a human-readable form
// that's painful to parse.View on GitHub (pinned to 64b1b85502)
Solutions
- Run `security delete-generic-password -s com.composio.cli -a default` manually (may require clicking Allow on the keychain prompt) and retry setSecret.
- If deletion is denied, open Keychain Access, find the item, and adjust its Access Control to allow your terminal/app, or delete it via the GUI.
- As a last resort, delete the whole item from Keychain Access and let setSecret recreate it.
Defensive patterns
Strategy: fallback
Try / catch
try {
await store.setSecret(service, user, secret);
} catch (e) {
if (e instanceof KeyringError && e.kind === 'PlatformFailure' && /duplicate/.test(String(e.cause))) {
// surface the manual `security delete-generic-password` instruction to the user
}
} Prevention
- Pre-create keychain items with an ACL that allows your build/CI identity to modify them.
- On shared Macs, verify you can run `security delete-generic-password -s com.composio.cli -a default` before automating secret writes.
When it happens
Trigger: setSecret on macOS when the existing com.composio.cli/default generic-password item exists, the `security delete-generic-password` call fails due to keychain ACL/permission restrictions, and the subsequent `security add-generic-password` returns EXIT_DUPLICATE_ITEM.
Common situations: Keychain items created by another agent/user or with custom access control; keychain locked or partition IDs missing after macOS upgrades; CI contexts where the security agent prompts are suppressed; secrets previously written by a different bundle identity.
Related errors
- PlatformFailure
- dlsym(${name}) failed
- CFStringCreateWithBytes returned NULL
- CFDataGetBytePtr returned NULL
- CFDictionaryCreateMutable returned NULL
AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28).
Data as JSON: /api/errors/92560d8d904f5fc0.
Report an issue: GitHub.