{"record":{"id":"92a7672b8e548964","repo":"srbhr/Resume-Matcher","slug":"failed-to-retry-processing-status-res-status","errorCode":null,"errorMessage":"Failed to retry processing (status ${res.status}): ${text}","messagePattern":"Failed to retry processing \\(status (.+?)\\): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/frontend/lib/api/resume.ts","lineNumber":380,"sourceCode":"}\n\n/** Generates interview preparation on-demand for a tailored resume */\nexport async function generateInterviewPrep(resumeId: string): Promise<InterviewPrepData> {\n  const res = await apiPost(`/resumes/${encodeURIComponent(resumeId)}/generate-interview-prep`, {});\n  if (!res.ok) {\n    const text = await res.text().catch(() => '');\n    throw new Error(`Failed to generate interview preparation (status ${res.status}): ${text}`);\n  }\n  const data = (await res.json()) as { interview_prep: InterviewPrepData };\n  return data.interview_prep;\n}\n\n/** Retries AI processing for a failed resume */\nexport async function retryProcessing(resumeId: string): Promise<ResumeUploadResponse> {\n  const res = await apiPost(`/resumes/${encodeURIComponent(resumeId)}/retry-processing`, {});\n  if (!res.ok) {\n    const text = await res.text().catch(() => '');\n    throw new Error(`Failed to retry processing (status ${res.status}): ${text}`);\n  }\n  return res.json();\n}\n\n/** Fetches the job description used to tailor a resume */\nexport async function fetchJobDescription(\n  resumeId: string\n): Promise<{ job_id: string; content: string }> {\n  const res = await apiFetch(`/resumes/${encodeURIComponent(resumeId)}/job-description`);\n  if (!res.ok) {\n    const text = await res.text().catch(() => '');\n    throw new Error(`Failed to fetch job description (status ${res.status}): ${text}`);\n  }\n  return res.json();\n}\n","sourceCodeStart":362,"sourceCodeEnd":396,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/frontend/lib/api/resume.ts#L362-L396","documentation":"retryProcessing calls POST /resumes/{id}/retry-processing to re-run AI processing for a failed resume, and throws this Error on any non-OK status. The response body text is appended to the message so the backend's failure detail is visible to the caller.","triggerScenarios":"Non-2xx from the retry-processing endpoint: resume not found (404), backend refuses retry because the resume is not in a failed state (409/400), auth expired (401), or the AI pipeline itself fails again immediately (500/502).","commonSituations":"Users click 'Retry processing' on a resume record that was already fixed or deleted; the backend LLM quota is exhausted; or the frontend session token expired between page load and the click.","solutions":["Check status/text in the message for the server reason","Confirm the resume is actually in a 'failed' state and still exists before retrying","Re-authenticate for 401/403 responses","If the retry succeeds but processing fails again, inspect backend AI provider configuration/credentials/quota","Surface the server text to the end user so they know whether retrying again will help"],"exampleFix":"// before\nawait retryProcessing(resumeId);\n// after\ntry {\n  await retryProcessing(resumeId);\n} catch (e) {\n  showError('Retry failed: ' + e.message + '. If the problem persists, contact support.');\n}","handlingStrategy":"retry","validationCode":"const status = await getResumeStatus(resumeId);\nif (status.state !== 'failed') {\n  throw new Error(`Retry only applies to failed resumes (state: ${status.state})`);\n}\nif (!resumeId) throw new Error('resumeId is required');","typeGuard":"function isRetryableFailure(e: unknown): boolean {\n  return e instanceof Error && /status 5\\d\\d/.test(e.message);\n}","tryCatchPattern":"async function retryWithBackoff(resumeId: string, attempts = 3): Promise<ResumeUploadResponse> {\n  for (let i = 0; i < attempts; i++) {\n    try {\n      return await retryProcessing(resumeId);\n    } catch (e) {\n      if (i === attempts - 1 || !isRetryableFailure(e)) throw e;\n      await new Promise(r => setTimeout(r, 2 ** i * 1000));\n    }\n  }\n  throw new Error('unreachable');\n}","preventionTips":["Only enable the retry button for resumes in a 'failed' state","Use exponential backoff on 5xx, never retry 4xx","Notify the user when retries are exhausted instead of looping silently","Monitor backend AI provider quota/credentials so retries are not doomed to fail","Show the server's error text from the message for actionable diagnostics"],"tags":["http-error","api","retry","frontend"],"backgroundTag":"http-non-2xx-response","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}