{"record":{"id":"704ea93e7f2c4b59","repo":"srbhr/Resume-Matcher","slug":"data-detail-failed-to-update-api-keys-status","errorCode":null,"errorMessage":"${data.detail || Failed to update API keys (status ${res.status}).}","messagePattern":"\\$\\{data\\.detail \\|\\| Failed to update API keys \\(status \\$\\{res\\.status\\}\\)\\.\\}","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/frontend/lib/api/config.ts","lineNumber":500,"sourceCode":"  if (!res.ok) {\n    throw new Error(`Failed to load API key status (status ${res.status}).`);\n  }\n\n  return res.json();\n}\n\n// Update API keys for one or more providers\nexport async function updateApiKeys(keys: ApiKeysUpdateRequest): Promise<ApiKeysUpdateResponse> {\n  const res = await apiFetch('/config/api-keys', {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    credentials: 'include',\n    body: JSON.stringify(keys),\n  });\n\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","sourceCodeStart":482,"sourceCodeEnd":518,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/frontend/lib/api/config.ts#L482-L518","documentation":"updateApiKeys (saving per-provider keys via the /config/api-keys endpoint) throws this when the response is not ok. It prefers the backend's `detail` string (FastAPI validation/auth message) and falls back to the generic status-code text when the body isn't JSON. Called from the settings key-management `handleSave`.","triggerScenarios":"Non-2xx on the API-keys update: 401/403 (session expired, CSRF), 422 (payload violates schema — e.g. unknown provider id or malformed key object), 400 (backend rejects a key, e.g. provider-specific format check or encryption failure), 404 (proxy/route misconfiguration), 500 (key store write failed).","commonSituations":"Pasting a provider key into the wrong provider slot; saving right after the session timed out; backend upgraded with stricter key validation than the running frontend expects; disk/DB issue preventing persistence of the encrypted key.","solutions":["Read the surfaced detail: it usually names the offending provider/field — correct the key entry and resubmit.","401/403 → re-authenticate then retry the save; 404 → fix proxy rewrites/BACKEND_ORIGIN; 5xx → check backend key-store logs.","Validate the key format client-side against PROVIDER_INFO (prefix/length expectations) before calling updateApiKeys.","Catch in handleSave and surface the message next to the key form so the user can fix input without losing edits."],"exampleFix":"// before\nawait updateApiKeys({ openai: keyInput });\n\n// after\nif (!/^sk-/.test(keyInput.trim())) {\n  setKeyError('OpenAI keys start with sk-');\n  return;\n}\ntry {\n  await updateApiKeys({ openai: keyInput.trim() });\n} catch (e) {\n  setKeyError(e instanceof Error ? e.message : 'Save failed');\n}","handlingStrategy":"validation","validationCode":"const SUPPORTED = ['openai', 'openai_compatible', 'azure_foundry', 'anthropic', 'openrouter', 'gemini', 'deepseek', 'groq', 'ollama'];\nif (Object.keys(keys).some((p) => !SUPPORTED.includes(p))) {\n  throw new Error('Unknown provider in key update');\n}","typeGuard":"function isProviderKeys(x: unknown): x is Record<string, string> {\n  return typeof x === 'object' && x !== null &&\n    Object.values(x).every((v) => typeof v === 'string');\n}","tryCatchPattern":"try {\n  await updateApiKeys(keys);\n} catch (e) {\n  const msg = e instanceof Error ? e.message : 'Save failed';\n  setKeyError(msg); // backend detail surfaces here\n}","preventionTips":["Validate key format per provider before submit (prefix/length heuristics)","Trim whitespace/newlines from pasted keys","Disable save while unauthenticated or session is near expiry","Keep PROVIDER_INFO in sync with the backend provider enum"],"tags":["network","http","api-keys","validation"],"backgroundTag":"http-non-2xx-response","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}