{"record":{"id":"0db9cdfcba21fa0d","repo":"srbhr/Resume-Matcher","slug":"improve-failed-with-status-response-status-t","errorCode":null,"errorMessage":"Improve failed with status ${response.status}: ${text}","messagePattern":"Improve failed with status (.+?): (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/frontend/lib/api/resume.ts","lineNumber":134,"sourceCode":"\nasync function postImprove(\n  endpoint: string,\n  payload: Record<string, unknown>\n): Promise<ImprovedResult> {\n  let response: Response;\n  try {\n    // Use the configurable request timeout so NEXT_PUBLIC_REQUEST_TIMEOUT_MS\n    // actually applies to the long-running improve/preview/confirm calls (#776).\n    response = await apiPost(endpoint, payload, DEFAULT_TIMEOUT_MS);\n  } catch (networkError) {\n    console.error(`Network error during ${endpoint}:`, networkError);\n    throw networkError;\n  }\n\n  const text = await response.text();\n  if (!response.ok) {\n    console.error('Improve failed response body:', text);\n    throw new Error(`Improve failed with status ${response.status}: ${text}`);\n  }\n\n  try {\n    return JSON.parse(text) as ImprovedResult;\n  } catch (parseError) {\n    console.error('Failed to parse improve response:', parseError, 'Raw response:', text);\n    throw parseError;\n  }\n}\n\n/** Uploads job descriptions and returns a job_id */\nexport async function uploadJobDescriptions(\n  descriptions: string[],\n  resumeId: string\n): Promise<string> {\n  const res = await apiPost('/jobs/upload', {\n    job_descriptions: descriptions,\n    resume_id: resumeId,","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/frontend/lib/api/resume.ts#L116-L152","documentation":"postImprove in apps/frontend/lib/api/resume.ts throws this Error when the improve endpoint returns a non-OK HTTP status, embedding both the status code and the FULL raw response body in the message. The body is also logged via console.error beforehand. Because the raw body is included, the message can be very long or contain HTML/JSON internals.","triggerScenarios":"The fetch (after a prior network failure was rethrown) resolves with response.ok === false: 401 auth failure, 404 unknown resume, 422 request payload fails schema validation, 429 rate limit, or 5xx from the AI improvement backend. It then reads the full text and throws with status plus body.","commonSituations":"Gateway/proxy returns an HTML error page (502/504) flooding the error message; request schema drifted from backend after an API update (422); auth cookie expired (401); LLM provider out of quota producing 500.","solutions":["Read the status and body in the thrown message (or console output) to classify: 401 re-auth, 404 verify resumeId, 422 fix payload schema, 5xx check backend/AI provider.","For 422, validate the improve request payload against the current API schema before sending.","Retry with backoff only for 429/502/503/504; surface a clean message to users instead of the raw body.","Check backend logs and AI provider configuration (keys, quota, timeout) for persistent 5xx.","Verify the API base URL/route matches the deployed backend version."],"exampleFix":"// before\nconst text = await response.text();\nif (!response.ok) {\n  console.error('Improve failed response body:', text);\n  throw new Error(`Improve failed with status ${response.status}: ${text}`);\n}\n// after\nconst text = await response.text();\nif (!response.ok) {\n  console.error('Improve failed response body:', text);\n  let detail = '';\n  try { detail = JSON.parse(text)?.detail ?? ''; } catch { /* non-JSON */ }\n  throw new Error(detail || `Improve failed with status ${response.status}`);\n}","handlingStrategy":"try-catch","validationCode":"if (!resumeId?.trim()) throw new Error('resumeId is required for improve.');","typeGuard":"function isImproveError(e: unknown): e is Error & { status?: number } {\n  return e instanceof Error && e.message.startsWith('Improve failed with status');\n}","tryCatchPattern":"try {\n  const improved = await improveResume(resumeId);\n} catch (e) {\n  if (isImproveError(e)) {\n    const status = Number(e.message.match(/status (\\d+)/)?.[1] ?? 0);\n    if (status === 401) redirectToLogin();\n    else if (status === 429 || status >= 500) showToast('Service busy — retrying shortly.');\n    else showToast('Improvement failed. Please check your input and retry.');\n  } else throw e;\n}","preventionTips":["Parse status out of the message or, better, attach status to a custom error class at the API layer.","Do not display the raw message to users — it can include the full HTML/JSON response body.","Retry with backoff on 429/5xx only; handle 401 with re-authentication."],"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"}