{"record":{"id":"6cb8b5ec754fceac","repo":"jlcodes99/cockpit-tools","slug":"api-key-exists","errorCode":"API_KEY_EXISTS","errorMessage":"API_KEY_EXISTS","messagePattern":"API_KEY_EXISTS","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/codexModelProviderService.ts","lineNumber":826,"sourceCode":"export async function updateApiKeyOnCodexModelProvider(\n  providerId: string,\n  apiKeyId: string,\n  apiKey: string,\n  name?: string,\n): Promise<CodexModelProvider> {\n  const normalizedApiKey = sanitizeApiKey(apiKey);\n  if (!normalizedApiKey) throw new Error(\"API_KEY_REQUIRED\");\n\n  const providers = await ensureProvidersLoaded();\n  const provider = providers.find((item) => item.id === providerId);\n  if (!provider) throw new Error(\"PROVIDER_NOT_FOUND\");\n  const existing = provider.apiKeys.find((item) => item.id === apiKeyId);\n  if (!existing) throw new Error(\"API_KEY_NOT_FOUND\");\n\n  const duplicate = provider.apiKeys.some(\n    (item) => item.id !== apiKeyId && sanitizeApiKey(item.apiKey) === normalizedApiKey,\n  );\n  if (duplicate) throw new Error(\"API_KEY_EXISTS\");\n\n  const now = Date.now();\n  existing.apiKey = normalizedApiKey;\n  if (name !== undefined) {\n    existing.name = sanitizeName(name);\n  }\n  existing.updatedAt = now;\n  provider.updatedAt = now;\n  await writeProviders(providers);\n  return { ...provider, apiKeys: provider.apiKeys.map((item) => ({ ...item })) };\n}\n\nexport async function testCodexModelProviderConnection(input: {\n  baseUrl: string;\n  apiKey: string;\n  wireApi?: CodexProviderWireApi | null;\n}): Promise<CodexLocalAccessTestResult> {\n  return await invoke('codex_test_model_provider_connection', {","sourceCodeStart":808,"sourceCodeEnd":844,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/services/codexModelProviderService.ts#L808-L844","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait updateProviderApiKey(providerId, keyA.id, keyB.apiKey); // duplicate\n// after\nawait deleteProviderApiKey(providerId, keyB.id);\nawait updateProviderApiKey(providerId, keyA.id, newSecret); // unique value","handlingStrategy":"validation","validationCode":"const provider = await getCodexModelProvider(providerId);\nconst normalized = secret.trim();\nif (provider.apiKeys.some(k => k.id !== apiKeyId && k.apiKey.trim() === normalized)) {\n  throw new Error('another key under this provider already stores this secret');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await updateProviderApiKey(providerId, apiKeyId, secret);\n} catch (e) {\n  if (e.message === 'API_KEY_EXISTS') {\n    console.error('Duplicate secret: delete the other key entry or supply a unique value.');\n  } else throw e;\n}","preventionTips":["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."],"tags":["api-key","duplicate","uniqueness-constraint"],"backgroundTag":"duplicate-resource-conflict","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}