{"record":{"id":"d3d933cc245c09d9","repo":"amruthpillai/reactive-resume","slug":"not-found","errorCode":"NOT_FOUND","errorMessage":"No cover letter found for this resume","messagePattern":"No cover letter found for this resume","errorType":"exception","errorClass":"ORPCError","httpStatus":404,"severity":"error","filePath":"packages/api/src/features/resume/export.ts","lineNumber":28,"sourceCode":"\nexport {\n\tcreateResumePdfDownloadUrl,\n\tMAX_PDF_DOWNLOAD_URL_TTL_SECONDS,\n\tverifyResumePdfDownloadToken,\n} from \"./pdf-download-url\";\n\ntype CreateResumePdfDownloadInput = {\n\tid: string;\n\tuserId: string;\n\ttarget?: ResumeExportTarget;\n};\n\nexport async function createResumePdfDownload(input: CreateResumePdfDownloadInput) {\n\tconst resume = await resumeService.getById({ id: input.id, userId: input.userId });\n\tconst data = parseStoredResumeData(resume.data);\n\tconst target = input.target ?? \"resume\";\n\tif (target === \"cover-letter\" && !resumeHasCoverLetter(data)) {\n\t\tthrow new ORPCError(\"NOT_FOUND\", { message: \"No cover letter found for this resume\" });\n\t}\n\n\tconst filename = generateFilename(target === \"cover-letter\" ? `${resume.name} Cover Letter` : resume.name, \"pdf\");\n\n\ttry {\n\t\t// Lazy-load the PDF renderer (@reactive-resume/pdf → @react-pdf/renderer +\n\t\t// phosphor-icons-react-pdf, ~10.6k icon modules) only when a PDF is actually\n\t\t// exported, instead of at server boot. Slashes cold-start file I/O on\n\t\t// constrained/slow-disk hosts. See fork perf/lazy-load-pdf.\n\t\tconst { createResumePdfFile } = await import(\"@reactive-resume/pdf/server\");\n\t\tconst body = await createResumePdfFile({ data: getResumeExportData(data, target), filename });\n\n\t\treturn {\n\t\t\theaders: {\n\t\t\t\t\"content-disposition\": `attachment; filename=\"${filename}\"`,\n\t\t\t},\n\t\t\tbody,\n\t\t};","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/amruthpillai/reactive-resume/blob/3a5b12e2a40374a9571988701fcb75c5a1831c42/packages/api/src/features/resume/export.ts#L10-L46","documentation":"createResumePdfDownload() throws NOT_FOUND when target is 'cover-letter' but resumeHasCoverLetter(data) is false. The cover letter is an optional resume section; exporting it requires the resume to actually contain one. The check runs after loading the resume but before invoking the PDF renderer.","triggerScenarios":"GET /resumes/{id}/pdf?target=cover-letter on a resume whose data has no cover-letter section (or it is empty).","commonSituations":"A download menu that always offers 'cover letter' regardless of whether the user added one; a fresh template that doesn't include the section; a resume where the section was deleted.","solutions":["Call resumeHasCoverLetter(data) (or check data.sections.summary.coverLetter) before offering the cover-letter export.","Gate the UI option behind presence of the section.","Default target to 'resume' when no cover letter exists."],"exampleFix":"// before\ndownloadResumePdf({ id, target: 'cover-letter' });\n// after\nconst has = resumeHasCoverLetter(resumeData);\ndownloadResumePdf({ id, target: has ? 'cover-letter' : 'resume' });","handlingStrategy":"validation","validationCode":"import { resumeHasCoverLetter } from '@reactive-resume/resume/export-sections';\nconst target = input.target === 'cover-letter' && resumeHasCoverLetter(data) ? 'cover-letter' : 'resume';","typeGuard":"function hasCoverLetter(data): boolean {\n  return Boolean(data?.sections?.summary?.coverLetter);\n}","tryCatchPattern":"try {\n  await downloadResumePdf({ id, target: 'cover-letter' });\n} catch (e) {\n  if (e.code === 'NOT_FOUND') { /* fall back to resume export */ await downloadResumePdf({ id, target: 'resume' }); }\n  else throw e;\n}","preventionTips":["Gate the 'download cover letter' action on resumeHasCoverLetter(data).","Hide the option entirely when the section is absent or empty.","Default to target 'resume' when in doubt."],"tags":["resume","pdf-export","cover-letter","not-found"],"backgroundTag":null,"analyzedSha":"3a5b12e2a40374a9571988701fcb75c5a1831c42","analyzedAt":"2026-08-12T22:31:22.666Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}