{"record":{"id":"1f1a5c41e9020cc2","repo":"srbhr/Resume-Matcher","slug":"text-resume-wizard-turn-failed-with-status","errorCode":null,"errorMessage":"${text || Resume wizard turn failed with status ${response.status}}","messagePattern":"\\$\\{text \\|\\| Resume wizard turn failed with status \\$\\{response\\.status\\}\\}","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/frontend/lib/api/resume-wizard.ts","lineNumber":110,"sourceCode":"    step: 'intro',\n    resume_data: emptyResumeData(),\n    current_question: { text: INTRO_QUESTION, section: 'intro' },\n    history: [],\n    asked_count: 0,\n    inferred_skills: [],\n    is_complete: false,\n    progress: { current: 0, total: 8 },\n    warnings: [],\n  };\n}\n\nexport async function postResumeWizardTurn(\n  payload: ResumeWizardTurnRequest\n): Promise<ResumeWizardTurnResponse> {\n  const response = await apiPost('/resume-wizard/turn', payload);\n  if (!response.ok) {\n    const text = await response.text().catch(() => '');\n    throw new Error(text || `Resume wizard turn failed with status ${response.status}`);\n  }\n  return response.json();\n}\n\nexport async function finalizeResumeWizard(\n  state: ResumeWizardState\n): Promise<ResumeWizardFinalizeResponse> {\n  const response = await apiPost('/resume-wizard/finalize', { state });\n  if (!response.ok) {\n    const text = await response.text().catch(() => '');\n    throw new Error(text || `Resume wizard finalize failed with status ${response.status}`);\n  }\n  return response.json();\n}\n","sourceCodeStart":92,"sourceCodeEnd":125,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/frontend/lib/api/resume-wizard.ts#L92-L125","documentation":"postResumeWizardTurn in apps/frontend/lib/api/resume-wizard.ts throws this Error when the POST to `/resume-wizard/turn` returns a non-OK response. Unlike the enrichment helpers it reads the raw response TEXT (not JSON detail), so the thrown message may contain an HTML error page or a JSON string. It signals the conversational resume-wizard backend rejected or failed the turn.","triggerScenarios":"apiPost('/resume-wizard/turn', payload) resolves with response.ok === false: 401 expired session, 404 unknown wizard/resume state id, 422 payload fails schema validation (missing state, malformed messages), 429 AI rate limit, or 500/504 from the LLM turn processing.","commonSituations":"A gateway returns an HTML 502 page, so the user sees raw HTML inside the Error message; the wizard state was built by an older frontend version and no longer matches the schema (422); session cookie expired mid-conversation (401); LLM latency causes a gateway timeout (504).","solutions":["Log response.status and the text body; if it looks like HTML, the failure is at a proxy/gateway, not the API itself.","For 422, validate the ResumeWizardTurnRequest (state present, messages non-empty) before sending.","For 401, re-authenticate and retry the turn.","For 429/504, retry with backoff and check backend AI provider limits/timeout config.","Prefer parsing JSON detail when content-type is JSON so users see a clean message instead of raw text/HTML."],"exampleFix":"// before\nconst response = await apiPost('/resume-wizard/turn', payload);\nif (!response.ok) {\n  const text = await response.text().catch(() => '');\n  throw new Error(text || `Resume wizard turn failed with status ${response.status}`);\n}\n// after\nconst response = await apiPost('/resume-wizard/turn', payload);\nif (!response.ok) {\n  const text = await response.text().catch(() => '');\n  let detail = '';\n  try { detail = JSON.parse(text)?.detail ?? ''; } catch { /* non-JSON */ }\n  throw new Error(detail || `Resume wizard turn failed with status ${response.status}`);\n}","handlingStrategy":"try-catch","validationCode":"if (!payload?.state) throw new Error('Resume wizard turn requires a state object.');","typeGuard":"function isWizardTurnRequest(p: unknown): p is ResumeWizardTurnRequest {\n  return typeof p === 'object' && p !== null && 'state' in p;\n}","tryCatchPattern":"try {\n  const turn = await postResumeWizardTurn(payload);\n} catch (e) {\n  const msg = e instanceof Error ? e.message : '';\n  if (/<html|<!doctype/i.test(msg)) showError('Service temporarily unavailable. Please retry.');\n  else if (msg.includes('status 401')) redirectToLogin();\n  else showError('The assistant could not process your reply. Please try again.');\n}","preventionTips":["Never render raw error text from this function to users — it can contain HTML error pages.","Persist wizard state locally after each turn so failures do not lose conversation progress.","Check the gateway/proxy when messages contain HTML: the API itself may be healthy."],"tags":["http-error","fetch","ai","api-client"],"backgroundTag":"http-non-ok-response","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}