jlcodes99/cockpit-tools · error
API_KEY_EXISTS
API_KEY_EXISTS
Error message
API_KEY_EXISTS
What it means
Thrown during an API-key update when the new (sanitized) key value duplicates another key under the same provider, excluding the key being updated. The service enforces key uniqueness per provider to keep credential records distinct. The update is rejected before any mutation occurs.
Source
Thrown at src/services/codexModelProviderService.ts:826
export async function updateApiKeyOnCodexModelProvider(
providerId: string,
apiKeyId: string,
apiKey: string,
name?: string,
): Promise<CodexModelProvider> {
const normalizedApiKey = sanitizeApiKey(apiKey);
if (!normalizedApiKey) throw new Error("API_KEY_REQUIRED");
const providers = await ensureProvidersLoaded();
const provider = providers.find((item) => item.id === providerId);
if (!provider) throw new Error("PROVIDER_NOT_FOUND");
const existing = provider.apiKeys.find((item) => item.id === apiKeyId);
if (!existing) throw new Error("API_KEY_NOT_FOUND");
const duplicate = provider.apiKeys.some(
(item) => item.id !== apiKeyId && sanitizeApiKey(item.apiKey) === normalizedApiKey,
);
if (duplicate) throw new Error("API_KEY_EXISTS");
const now = Date.now();
existing.apiKey = normalizedApiKey;
if (name !== undefined) {
existing.name = sanitizeName(name);
}
existing.updatedAt = now;
provider.updatedAt = now;
await writeProviders(providers);
return { ...provider, apiKeys: provider.apiKeys.map((item) => ({ ...item })) };
}
export async function testCodexModelProviderConnection(input: {
baseUrl: string;
apiKey: string;
wireApi?: CodexProviderWireApi | null;
}): Promise<CodexLocalAccessTestResult> {
return await invoke('codex_test_model_provider_connection', {View on GitHub (pinned to 1ed8b77992)
Solutions
- Use a distinct secret value that no other key under this provider stores.
- If the intent is to replace key B, delete key B first or update key B itself instead.
- Trim the input — if you only meant cosmetic changes (name), update the name field, not the key value.
Example fix
// before await updateProviderApiKey(providerId, keyA.id, keyB.apiKey); // duplicate // after await deleteProviderApiKey(providerId, keyB.id); await updateProviderApiKey(providerId, keyA.id, newSecret); // unique value
Defensive patterns
Strategy: validation
Validate before calling
const provider = await getCodexModelProvider(providerId);
const normalized = secret.trim();
if (provider.apiKeys.some(k => k.id !== apiKeyId && k.apiKey.trim() === normalized)) {
throw new Error('another key under this provider already stores this secret');
} Try / catch
try {
await updateProviderApiKey(providerId, apiKeyId, secret);
} catch (e) {
if (e.message === 'API_KEY_EXISTS') {
console.error('Duplicate secret: delete the other key entry or supply a unique value.');
} else throw e;
} Prevention
- Keep one secret per named key entry per provider.
- Rotate by updating the existing key, not by creating duplicates.
- Trim secrets before saving so whitespace variants don't slip through.
When it happens
Trigger: Updating key A with the value of key B under the same provider (item.id !== apiKeyId and sanitized values equal); pasting the same secret into two key entries; whitespace-only differences that sanitizeApiKey collapses to the same value.
Common situations: Copy-paste of the same token twice into different named keys; rotating a key to a value already stored as another entry; attempts to 'rename' a key by updating another entry with the same secret.
Related errors
- API_KEY_NOT_FOUND
- API_KEY_REQUIRED
- SUB2API_API_KEY_MISSING
- Official auth.json requires OPENAI_API_KEY for API Key accou
- accounts.groups.error.duplicate
AI-assisted analysis of jlcodes99/cockpit-tools@1ed8b77992 (2026-09-05).
Data as JSON: /api/errors/6cb8b5ec754fceac.
Report an issue: GitHub.