{"record":{"id":"42d2ee53a8ed77ad","repo":"srbhr/Resume-Matcher","slug":"failed-to-load-feature-prompts-status-res-statu","errorCode":null,"errorMessage":"Failed to load feature prompts (status ${res.status}).","messagePattern":"Failed to load feature prompts \\(status (.+?)\\)\\.","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/frontend/lib/api/config.ts","lineNumber":369,"sourceCode":"  code: 'missing_placeholders';\n  field: 'cover_letter_prompt' | 'outreach_message_prompt';\n  missing: string[];\n}\n\nexport class FeaturePromptsError extends Error {\n  detail: FeaturePromptsValidationError;\n\n  constructor(detail: FeaturePromptsValidationError) {\n    super(`Invalid ${detail.field}: missing ${detail.missing.join(', ')}`);\n    this.name = 'FeaturePromptsError';\n    this.detail = detail;\n  }\n}\n\nexport async function fetchFeaturePrompts(): Promise<FeaturePrompts> {\n  const res = await apiFetch('/config/feature-prompts', { credentials: 'include' });\n  if (!res.ok) {\n    throw new Error(`Failed to load feature prompts (status ${res.status}).`);\n  }\n  return res.json();\n}\n\nexport async function updateFeaturePrompts(update: FeaturePromptsUpdate): Promise<FeaturePrompts> {\n  const res = await apiFetch('/config/feature-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    // Error path: body may be absent or malformed, so we tolerate parse\n    // failure. A fetch body is a one-shot stream — read it once and reuse\n    // for both the 422-special-case and the generic fallback.\n    const errBody = (await res.json().catch(() => ({}))) as {\n      detail?: FeaturePromptsValidationError | string;","sourceCodeStart":351,"sourceCodeEnd":387,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/frontend/lib/api/config.ts#L351-L387","documentation":"fetchFeaturePrompts performs GET /config/feature-prompts and throws this generic Error whenever the HTTP response is not ok. Like the other config fetchers it only surfaces the status code; the actual cause (auth, routing, backend failure) must be inferred from it. The typed FeaturePrompts result is only returned on a 2xx with a JSON body.","triggerScenarios":"Non-2xx on GET /config/feature-prompts: 401/403 from expired credentials cookie or CSRF check, 404 because the backend route is missing or NEXT_PUBLIC_API_URL/BACKEND_ORIGIN proxying is wrong, 5xx when the backend cannot read its feature-prompt store (e.g. DB/config file unavailable).","commonSituations":"Opening Settings while the backend container is restarting (502 through the proxy); stale deployment where frontend and backend versions disagree and the endpoint moved; running the frontend without the backend on :8000 in local dev.","solutions":["Check the status in the message: 401/403 → refresh the session; 404 → confirm /api/v1/config/feature-prompts exists on the backend and rewrites point at BACKEND_ORIGIN; 5xx → inspect backend logs.","Curl the endpoint with the session cookie to separate proxy problems from backend problems.","Verify frontend/backend versions are matched (endpoint present in the deployed backend).","Handle the rejection where the callers (llmConfig/featureConfig/promptConfig/featurePrompts/keyStatus loaders) run so the page degrades gracefully."],"exampleFix":"// before\nconst prompts = await fetchFeaturePrompts();\n\n// after\nlet prompts: FeaturePrompts;\ntry {\n  prompts = await fetchFeaturePrompts();\n} catch (e) {\n  console.warn('feature prompts unavailable, using defaults', e);\n  prompts = DEFAULT_FEATURE_PROMPTS; // fallback\n}","handlingStrategy":"fallback","validationCode":"// Health-check the backend before dependent config fetches\nconst ping = await fetch('/api/v1/status', { credentials: 'include' });\nif (!ping.ok) throw new Error('Backend unavailable, skip feature prompts load');","typeGuard":"function isFeaturePrompts(x: unknown): x is FeaturePrompts {\n  return typeof x === 'object' && x !== null && 'cover_letter_prompt' in x;\n}","tryCatchPattern":"try {\n  prompts = await fetchFeaturePrompts();\n} catch {\n  prompts = DEFAULT_FEATURE_PROMPTS;\n}","preventionTips":["Verify backend reachability before loading settings","Ship default feature prompts as client-side fallback","Match frontend and backend deployment versions","Alert on 404s from proxied /api routes (proxy misconfiguration signal)"],"tags":["network","http","frontend-api-client","config"],"backgroundTag":"http-non-2xx-response","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}