srbhr/Resume-Matcher · error · Error
Failed to download cover letter (status ${res.status}): ${te
Error message
Failed to download cover letter (status ${res.status}): ${text} What it means
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.
Source
Thrown at apps/frontend/lib/api/resume.ts:337
): string {
const normalizedId = normalizeResumeId(resumeId);
const params = new URLSearchParams({ pageSize });
if (locale) {
params.set('lang', locale);
}
return `${API_BASE}/resumes/${encodeURIComponent(normalizedId)}/cover-letter/pdf?${params.toString()}`;
}
export async function downloadCoverLetterPdf(
resumeId: string,
pageSize: 'A4' | 'LETTER' = 'A4',
locale?: Locale
): Promise<Blob> {
const url = getCoverLetterPdfUrl(resumeId, pageSize, locale);
const res = await apiFetch(url);
if (!res.ok) {
const text = await res.text().catch(() => '');
throw new Error(`Failed to download cover letter (status ${res.status}): ${text}`);
}
return await res.blob();
}
/** Generates a cover letter on-demand for a tailored resume */
export async function generateCoverLetter(resumeId: string): Promise<string> {
const res = await apiPost(`/resumes/${encodeURIComponent(resumeId)}/generate-cover-letter`, {});
if (!res.ok) {
const text = await res.text().catch(() => '');
throw new Error(`Failed to generate cover letter (status ${res.status}): ${text}`);
}
const data = await res.json();
return data.content;
}
/** Generates an outreach message on-demand for a tailored resume */
export async function generateOutreachMessage(resumeId: string): Promise<string> {
const res = await apiPost(`/resumes/${encodeURIComponent(resumeId)}/generate-outreach`, {});View on GitHub (pinned to 116f9cc3b0)
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.
Example fix
// before
const blob = await downloadCoverLetterPdf(resumeId, pageSize, locale);
// after
let blob;
try {
blob = await downloadCoverLetterPdf(resumeId, pageSize, locale);
} catch (err) {
if (String(err.message).includes('status 404')) {
await generateCoverLetter(resumeId);
blob = await downloadCoverLetterPdf(resumeId, pageSize, locale);
} else {
notifyUser('Cover letter download failed. Please try again.');
}
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!resumeId) {
throw new Error('Cannot download cover letter: resumeId is required');
}
const allowedPageSizes = ['a4', 'letter'];
if (pageSize && !allowedPageSizes.includes(pageSize)) {
throw new Error(`Unsupported pageSize: ${pageSize}`);
} Try / catch
try {
const blob = await downloadCoverLetterPdf(resumeId, pageSize, locale);
triggerBlobDownload(blob);
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
if (msg.includes('status 404')) notifyUser('No cover letter yet — generate one first.');
else if (msg.includes('status 401')) promptRelogin();
else notifyUser('Download failed. Please retry.');
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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).
Related errors
- Failed to update cover letter (status ${res.status}): ${text
- Failed to update outreach message (status ${res.status}): ${
- Failed to rename resume (status ${res.status}): ${text}
- Failed to generate cover letter (status ${res.status}): ${te
- Upload failed for ${fileToUpload.file.name}. Status: ${respo
AI-assisted analysis of srbhr/Resume-Matcher@116f9cc3b0 (2026-08-28).
Data as JSON: /api/errors/95485698ba91a2bd.
Report an issue: GitHub.