{"record":{"id":"e31a21b14f3adad0","repo":"srbhr/Resume-Matcher","slug":"data-detail-failed-to-update-prompt-config-s","errorCode":null,"errorMessage":"${data.detail || Failed to update prompt config (status ${res.status}).}","messagePattern":"\\$\\{data\\.detail \\|\\| Failed to update prompt config \\(status \\$\\{res\\.status\\}\\)\\.\\}","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/frontend/lib/api/config.ts","lineNumber":328,"sourceCode":"  if (!res.ok) {\n    throw new Error(`Failed to load prompt config (status ${res.status}).`);\n  }\n\n  return res.json();\n}\n\n// Update prompt configuration\nexport async function updatePromptConfig(update: PromptConfigUpdate): Promise<PromptConfig> {\n  const res = await apiFetch('/config/prompts', {\n    method: 'PUT',\n    headers: { 'Content-Type': 'application/json' },\n    credentials: 'include',\n    body: JSON.stringify(update),\n  });\n\n  if (!res.ok) {\n    const data = await res.json().catch(() => ({}));\n    throw new Error(data.detail || `Failed to update prompt config (status ${res.status}).`);\n  }\n\n  return res.json();\n}\n\n// Custom feature prompts (cover letter, cold outreach)\nexport interface FeaturePrompts {\n  cover_letter_prompt: string;\n  outreach_message_prompt: string;\n  cover_letter_default: string;\n  outreach_message_default: string;\n}\n\nexport interface FeaturePromptsUpdate {\n  cover_letter_prompt?: string;\n  outreach_message_prompt?: string;\n}\n","sourceCodeStart":310,"sourceCodeEnd":346,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/frontend/lib/api/config.ts#L310-L346","documentation":"updatePromptConfig (PUT/POST to /config/prompts) throws this when the response is not ok. It first tries res.json() to extract a backend `detail` message (FastAPI convention) and falls back to the generic status-code message if the body isn't JSON. So the message is either the server's explanation or 'Failed to update prompt config (status N).' Note the `||` shortcut: an object `detail` would stringify badly — for structured details prefer explicit serialization.","triggerScenarios":"Non-2xx response from the prompt-config update endpoint: 401/403 (expired session cookie, CSRF failure), 422 (FastAPI validation rejected the PromptConfigUpdate payload, e.g. invalid field value), 404 (route missing/misconfigured proxy), 500 (backend failed persisting config).","commonSituations":"Submitting the settings form after the session expired; sending an update shape that no longer matches the backend Pydantic schema after a version upgrade; backend temporarily down during save; proxy misroute returning an HTML 404 page (then data.detail is undefined and the fallback message shows).","solutions":["Read the surfaced `detail` (or status code): 422 → fix the payload to match the backend PromptConfigUpdate schema; 401/403 → re-authenticate and retry; 5xx → check backend logs.","Verify the update object only contains fields the backend accepts (provider, model, api_key, api_base, reasoning_effort etc.) after any API version change.","Confirm the Next.js proxy rewrite for /api targets the running backend origin.","Wrap the call in try/catch in the calling handler (e.g. `updated`) and show the message in the UI instead of letting it bubble as an unhandled rejection."],"exampleFix":"// before\nawait updatePromptConfig(update);\n\n// after\ntry {\n  await updatePromptConfig(update);\n} catch (e) {\n  setSaveError(e instanceof Error ? e.message : 'Unknown error saving prompt config');\n}","handlingStrategy":"try-catch","validationCode":"const allowedKeys = ['provider', 'model', 'api_key', 'api_base', 'reasoning_effort'];\nif (Object.keys(update).some((k) => !allowedKeys.includes(k))) {\n  throw new Error('PromptConfigUpdate contains unknown fields');\n}","typeGuard":"function isRecord(x: unknown): x is Record<string, unknown> {\n  return typeof x === 'object' && x !== null && !Array.isArray(x);\n}","tryCatchPattern":"try {\n  await updatePromptConfig(update);\n} catch (e) {\n  const msg = e instanceof Error ? e.message : String(e);\n  setSaveError(/status 4\\d\\d/.test(msg) ? 'Check your session and input, then retry.' : msg);\n}","preventionTips":["Keep frontend payload types in sync with backend Pydantic schemas after upgrades","Re-authenticate on 401 instead of resubmitting blindly","Check the network tab for the real status/detail when the message is generic","Show save errors inline in the settings form"],"tags":["network","http","validation","frontend-api-client"],"backgroundTag":"http-non-2xx-response","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}