{"record":{"id":"2347a4c1252098f3","repo":"srbhr/Resume-Matcher","slug":"data-detail-failed-to-delete-api-key-status","errorCode":null,"errorMessage":"${data.detail || Failed to delete API key (status ${res.status}).}","messagePattern":"\\$\\{data\\.detail \\|\\| Failed to delete API key \\(status \\$\\{res\\.status\\}\\)\\.\\}","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/frontend/lib/api/config.ts","lineNumber":515,"sourceCode":"\n  if (!res.ok) {\n    const data = await res.json().catch(() => ({}));\n    throw new Error(data.detail || `Failed to update API keys (status ${res.status}).`);\n  }\n\n  return res.json();\n}\n\n// Delete API key for a specific provider\nexport async function deleteApiKey(provider: ApiKeyProvider): Promise<void> {\n  const res = await apiFetch(`/config/api-keys/${provider}`, {\n    method: 'DELETE',\n    credentials: 'include',\n  });\n\n  if (!res.ok) {\n    const data = await res.json().catch(() => ({}));\n    throw new Error(data.detail || `Failed to delete API key (status ${res.status}).`);\n  }\n}\n\n// Clear all API keys\nexport async function clearAllApiKeys(): Promise<void> {\n  const res = await apiFetch('/config/api-keys?confirm=CLEAR_ALL_KEYS', {\n    method: 'DELETE',\n    credentials: 'include',\n  });\n\n  if (!res.ok) {\n    const data = await res.json().catch(() => ({}));\n    throw new Error(data.detail || `Failed to clear API keys (status ${res.status}).`);\n  }\n}\n\n// Reset database\nexport async function resetDatabase(): Promise<void> {","sourceCodeStart":497,"sourceCodeEnd":533,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/frontend/lib/api/config.ts#L497-L533","documentation":"deleteApiKey issues DELETE for one provider's key on /config/api-keys and throws this when the response is not ok, preferring the backend `detail` string and falling back to the status-code message. It has no return value, so the throw is the only failure signal to handleDeleteApiKey.","triggerScenarios":"Non-2xx on DELETE /config/api-keys/<provider>: 401/403 (expired session/CSRF), 404 (the provider has no stored key, or the route/provider id is unknown — mismatched frontend/backend provider enums), 422 (malformed provider identifier), 500 (key-store deletion failed).","commonSituations":"Deleting a key that was already removed in another tab (404); session expired between page load and the delete click; backend upgrade renamed a provider slug; DB lock preventing the delete.","solutions":["404 → treat as 'already deleted' or verify the provider id matches PROVIDER_INFO and the deployed backend's enum.","401/403 → re-authenticate and retry; 5xx → check backend logs for key-store errors.","Catch in handleDeleteApiKey, show the detail message, then refresh the key-status list so the UI matches server state.","Avoid double-clicking delete: disable the button while the request is in flight to prevent duplicate 404s."],"exampleFix":"// before\nawait deleteApiKey(provider);\nrefreshKeys();\n\n// after\ntry {\n  await deleteApiKey(provider);\n} catch (e) {\n  showToast(e instanceof Error ? e.message : 'Delete failed');\n} finally {\n  refreshKeys();\n}","handlingStrategy":"try-catch","validationCode":"if (!SUPPORTED.includes(provider)) {\n  throw new Error(`Unknown provider: ${provider}`);\n}","typeGuard":"function is deletableProvider(p: string): boolean {\n  return SUPPORTED.includes(p);\n}","tryCatchPattern":"try {\n  await deleteApiKey(provider);\n} catch (e) {\n  const msg = e instanceof Error ? e.message : '';\n  if (msg.includes('status 404')) {\n    showToast('Key was already removed');\n  } else {\n    showToast(msg || 'Delete failed');\n  }\n} finally {\n  refreshKeys();\n}","preventionTips":["Refresh key status after every delete to keep UI in sync","Disable the delete button while a delete is in flight","Verify provider ids against the deployed backend enum","Re-authenticate instead of retrying deletes after 401"],"tags":["network","http","api-keys","delete"],"backgroundTag":"http-non-2xx-response","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}