{"record":{"id":"1bb8829b7f7beb23","repo":"srbhr/Resume-Matcher","slug":"message-errbody-detail-or-failed-to-update-fea","errorCode":null,"errorMessage":"${message = errBody.detail or Failed to update feature prompts (status ${res.status}).}","messagePattern":"\\$\\{message = errBody\\.detail or Failed to update feature prompts \\(status \\$\\{res\\.status\\}\\)\\.\\}","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/frontend/lib/api/config.ts","lineNumber":407,"sourceCode":"    if (\n      res.status === 422 &&\n      typeof errBody.detail === 'object' &&\n      errBody.detail?.code === 'missing_placeholders'\n    ) {\n      throw new FeaturePromptsError(errBody.detail);\n    }\n    // FastAPI can return ``detail`` as a string or a structured object.\n    // Stringifying an object via the ``||`` shortcut yields \"[object Object]\";\n    // serialize explicitly.\n    let message: string;\n    if (typeof errBody.detail === 'string') {\n      message = errBody.detail;\n    } else if (errBody.detail) {\n      message = JSON.stringify(errBody.detail);\n    } else {\n      message = `Failed to update feature prompts (status ${res.status}).`;\n    }\n    throw new Error(message);\n  }\n\n  // Success path: require a valid JSON body. Swallowing parse errors here\n  // would let an invalid success response be returned as FeaturePrompts\n  // with undefined fields — caller code would then read .cover_letter_prompt\n  // and get surprising behavior. Let the parse error propagate.\n  return (await res.json()) as FeaturePrompts;\n}\n\n// API Key Management types\nexport type ApiKeyProvider =\n  | 'openai'\n  | 'azure_foundry'\n  | 'anthropic'\n  | 'google'\n  | 'openrouter'\n  | 'deepseek'\n  | 'groq'","sourceCodeStart":389,"sourceCodeEnd":425,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/frontend/lib/api/config.ts#L389-L425","documentation":"The generic branch of updateFeaturePrompts: for any non-422-missing_placeholders failure it builds a message from errBody.detail — string detail used as-is, object detail JSON.stringify'd (avoiding '[object Object]'), and this exact message as the final fallback when the body has no usable detail. It then throws a plain Error. It fires for auth failures, other 422 validation errors, 404 routing problems, and 5xx backend faults on PUT /config/feature-prompts.","triggerScenarios":"Non-ok response on POST/PUT /config/feature-prompts that isn't the 422 missing_placeholders case: 401/403 (session expired, CSRF), 422 with a different validation code, 404 (endpoint absent / proxy misroute), 500/502 (backend down or failed persisting prompts).","commonSituations":"Saving feature prompts after the auth cookie expired; deploying mismatched frontend/backend versions so the endpoint moved; infrastructure outage returning an HTML error page (res.json() catch yields {}, so the fallback text shows).","solutions":["Inspect the thrown message: a string detail is the backend's own explanation; a JSON-stringified object contains structured validation info; the fallback text means inspect res.status via logs/network tab.","401/403 → re-authenticate; 404 → fix BACKEND_ORIGIN/next.config.ts rewrites; 5xx → check backend logs.","For object details, parse the JSON in the message to locate the offending field and correct the FeaturePromptsUpdate payload.","Wrap the `fresh` refresh/save flow in try/catch so the error is displayed in the form rather than crashing the settings page."],"exampleFix":"// before\nawait updateFeaturePrompts(update);\n\n// after\ntry {\n  await updateFeaturePrompts(update);\n} catch (e) {\n  if (e instanceof FeaturePromptsError) {\n    showPlaceholderHints(e.detail);\n  } else {\n    showSaveError(e instanceof Error ? e.message : String(e));\n  }\n}","handlingStrategy":"try-catch","validationCode":"function isNonEmptyPromptUpdate(u: FeaturePromptsUpdate): boolean {\n  return Object.values(u).every((v) => typeof v === 'string' && v.trim().length > 0);\n}","typeGuard":"function hasStringDetail(x: unknown): x is { detail: string } {\n  return typeof x === 'object' && x !== null && typeof (x as any).detail === 'string';\n}","tryCatchPattern":"try {\n  await updateFeaturePrompts(update);\n} catch (e) {\n  if (e instanceof FeaturePromptsError) handlePlaceholders(e.detail);\n  else showError(e instanceof Error ? e.message : 'Unknown save failure');\n}","preventionTips":["Catch FeaturePromptsError separately from the generic Error branch","Inspect the network response body — the thrown message may embed JSON detail","Re-authenticate before retrying after 401/403","Keep frontend/backend versions aligned so the endpoint path exists"],"tags":["network","http","frontend-api-client","error-message"],"backgroundTag":"http-non-2xx-response","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}