{"record":{"id":"95485698ba91a2bd","repo":"srbhr/Resume-Matcher","slug":"failed-to-download-cover-letter-status-res-stat","errorCode":null,"errorMessage":"Failed to download cover letter (status ${res.status}): ${text}","messagePattern":"Failed to download cover letter \\(status (.+?)\\): (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/frontend/lib/api/resume.ts","lineNumber":337,"sourceCode":"): string {\n  const normalizedId = normalizeResumeId(resumeId);\n  const params = new URLSearchParams({ pageSize });\n  if (locale) {\n    params.set('lang', locale);\n  }\n  return `${API_BASE}/resumes/${encodeURIComponent(normalizedId)}/cover-letter/pdf?${params.toString()}`;\n}\n\nexport async function downloadCoverLetterPdf(\n  resumeId: string,\n  pageSize: 'A4' | 'LETTER' = 'A4',\n  locale?: Locale\n): Promise<Blob> {\n  const url = getCoverLetterPdfUrl(resumeId, pageSize, locale);\n  const res = await apiFetch(url);\n  if (!res.ok) {\n    const text = await res.text().catch(() => '');\n    throw new Error(`Failed to download cover letter (status ${res.status}): ${text}`);\n  }\n  return await res.blob();\n}\n\n/** Generates a cover letter on-demand for a tailored resume */\nexport async function generateCoverLetter(resumeId: string): Promise<string> {\n  const res = await apiPost(`/resumes/${encodeURIComponent(resumeId)}/generate-cover-letter`, {});\n  if (!res.ok) {\n    const text = await res.text().catch(() => '');\n    throw new Error(`Failed to generate cover letter (status ${res.status}): ${text}`);\n  }\n  const data = await res.json();\n  return data.content;\n}\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`, {});","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/frontend/lib/api/resume.ts#L319-L355","documentation":"downloadCoverLetterPdf() GETs the cover-letter PDF URL via apiFetch and throws when the response is not OK. The status and response body are embedded in the message. Unlike save errors, this path also fails on server-side PDF rendering problems.","triggerScenarios":"Calling downloadCoverLetterPdf(resumeId, pageSize, locale) when the API returns !res.ok: resume or cover letter missing (404), expired auth (401), unsupported pageSize/locale (400), or PDF generation failure (500).","commonSituations":"User clicks download before a cover letter has been generated (404); session expired (401); backend PDF renderer (e.g. headless browser) crashes or times out producing 5xx; gateway timeout on large documents (504).","solutions":["Check the embedded status/body: 404 means no cover letter exists yet; 401 means re-authenticate.","If 404: generate the cover letter first (generateCoverLetter) and then retry the download.","If 401: refresh the session and retry.","If 5xx/504: retry after a short delay; investigate the PDF renderer logs if persistent.","Surface a user-facing 'download failed' message and keep the button enabled for retry."],"exampleFix":"// before\nconst blob = await downloadCoverLetterPdf(resumeId, pageSize, locale);\n\n// after\nlet blob;\ntry {\n  blob = await downloadCoverLetterPdf(resumeId, pageSize, locale);\n} catch (err) {\n  if (String(err.message).includes('status 404')) {\n    await generateCoverLetter(resumeId);\n    blob = await downloadCoverLetterPdf(resumeId, pageSize, locale);\n  } else {\n    notifyUser('Cover letter download failed. Please try again.');\n  }\n}","handlingStrategy":"try-catch","validationCode":"if (!resumeId) {\n  throw new Error('Cannot download cover letter: resumeId is required');\n}\nconst allowedPageSizes = ['a4', 'letter'];\nif (pageSize && !allowedPageSizes.includes(pageSize)) {\n  throw new Error(`Unsupported pageSize: ${pageSize}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const blob = await downloadCoverLetterPdf(resumeId, pageSize, locale);\n  triggerBlobDownload(blob);\n} catch (err) {\n  const msg = err instanceof Error ? err.message : String(err);\n  if (msg.includes('status 404')) notifyUser('No cover letter yet — generate one first.');\n  else if (msg.includes('status 401')) promptRelogin();\n  else notifyUser('Download failed. Please retry.');\n}","preventionTips":["Ensure a cover letter exists (or generate it) before enabling the download button.","Validate pageSize/locale against supported values.","Handle 401 globally with session refresh.","Treat PDF-rendering 5xx as retryable and offer a retry action in the UI."],"tags":["http","api","pdf","download","frontend"],"backgroundTag":"http-non-ok-response","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}