{"record":{"id":"0ce1850e11bbd889","repo":"srbhr/Resume-Matcher","slug":"failed-to-generate-interview-preparation-status","errorCode":null,"errorMessage":"Failed to generate interview preparation (status ${res.status}): ${text}","messagePattern":"Failed to generate interview preparation \\(status (.+?)\\): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/frontend/lib/api/resume.ts","lineNumber":369,"sourceCode":"}\n\n/** Generates an outreach message on-demand for a tailored resume */\nexport async function generateOutreachMessage(resumeId: string): Promise<string> {\n  const res = await apiPost(`/resumes/${encodeURIComponent(resumeId)}/generate-outreach`, {});\n  if (!res.ok) {\n    const text = await res.text().catch(() => '');\n    throw new Error(`Failed to generate outreach message (status ${res.status}): ${text}`);\n  }\n  const data = await res.json();\n  return data.content;\n}\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","sourceCodeStart":351,"sourceCodeEnd":387,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/frontend/lib/api/resume.ts#L351-L387","documentation":"generateInterviewPrep calls POST /resumes/{id}/generate-interview-prep and throws this Error for any non-OK backend status, including the response body text in the message. It exists to surface server-side interview-prep generation failures as a typed exception rather than silently parsing bad JSON.","triggerScenarios":"Non-2xx from the generate-interview-prep endpoint: resume not found (404), failed/incomplete AI analysis (500/502/504), expired auth (401), or malformed resumeId (400 validation).","commonSituations":"Seen when interview prep is requested for a resume whose AI processing failed, when the LLM upstream times out producing the structured interview_prep data, after session expiry, or when testing with a hardcoded id that no longer exists.","solutions":["Read the status and text in the message for the server-side cause","Confirm the resume status is 'completed' before requesting interview prep","Re-login if status is 401/403","Retry after a delay for 5xx (AI provider load) or use the retry-processing endpoint if the resume is in a failed state","Verify the resumeId is valid and current"],"exampleFix":"// before\nconst prep = await generateInterviewPrep(resumeId);\n// after\ntry {\n  const prep = await generateInterviewPrep(resumeId);\n} catch (e) {\n  setPrepError('Interview prep is unavailable for this resume. Ensure processing completed, then retry.');\n}","handlingStrategy":"try-catch","validationCode":"if (!resumeId) throw new Error('resumeId is required');\nconst status = await getResumeStatus(resumeId);\nif (status.state !== 'completed') {\n  throw new Error(`Interview prep requires a completed resume (state: ${status.state})`);\n}","typeGuard":"function isInterviewPrepData(v: unknown): v is InterviewPrepData {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":"try {\n  const prep = await generateInterviewPrep(resumeId);\n  render(prep);\n} catch (e) {\n  if (e instanceof Error && /status 5\\d\\d/.test(e.message)) {\n    scheduleRetryWithBackoff();\n  } else {\n    showError('Interview prep unavailable. Ensure the resume finished processing.');\n  }\n}","preventionTips":["Only request interview prep after AI processing succeeds","Check resume state via the status endpoint before generating","Distinguish retryable (5xx) from permanent (4xx) failures by the status in the message","Handle session expiry centrally (401 interceptor)","Cache successful prep results to reduce repeat generation calls"],"tags":["http-error","api","fetch","frontend"],"backgroundTag":"http-non-2xx-response","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}