amruthpillai/reactive-resume · error · ORPCError

NOT_FOUND

NOT_FOUND

Error message

No cover letter found for this resume

What it means

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.

Solutions

  1. Call resumeHasCoverLetter(data) (or check data.sections.summary.coverLetter) before offering the cover-letter export.
  2. Gate the UI option behind presence of the section.
  3. Default target to 'resume' when no cover letter exists.

Example fix

// before
downloadResumePdf({ id, target: 'cover-letter' });
// after
const has = resumeHasCoverLetter(resumeData);
downloadResumePdf({ id, target: has ? 'cover-letter' : 'resume' });
Defensive patterns

Strategy: validation

Validate before calling

import { resumeHasCoverLetter } from '@reactive-resume/resume/export-sections';
const target = input.target === 'cover-letter' && resumeHasCoverLetter(data) ? 'cover-letter' : 'resume';

Type guard

function hasCoverLetter(data): boolean {
  return Boolean(data?.sections?.summary?.coverLetter);
}

Try / catch

try {
  await downloadResumePdf({ id, target: 'cover-letter' });
} catch (e) {
  if (e.code === 'NOT_FOUND') { /* fall back to resume export */ await downloadResumePdf({ id, target: 'resume' }); }
  else throw e;
}

Prevention

When it happens

Trigger: GET /resumes/{id}/pdf?target=cover-letter on a resume whose data has no cover-letter section (or it is empty).

Common situations: 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.

Understand the failure class

Background: NOT_FOUND error code: why tRPC, Harbor, Nacos and other libraries return 404 "not found" errors for resources that may still exist — this error's family across 11 libraries.

Related errors


AI-assisted analysis of amruthpillai/reactive-resume@3a5b12e2a4 (2026-08-12). Data as JSON: /api/errors/d3d933cc245c09d9. Report an issue: GitHub.

Appendix: source

Thrown at packages/api/src/features/resume/export.ts:28

export {
	createResumePdfDownloadUrl,
	MAX_PDF_DOWNLOAD_URL_TTL_SECONDS,
	verifyResumePdfDownloadToken,
} from "./pdf-download-url";

type CreateResumePdfDownloadInput = {
	id: string;
	userId: string;
	target?: ResumeExportTarget;
};

export async function createResumePdfDownload(input: CreateResumePdfDownloadInput) {
	const resume = await resumeService.getById({ id: input.id, userId: input.userId });
	const data = parseStoredResumeData(resume.data);
	const target = input.target ?? "resume";
	if (target === "cover-letter" && !resumeHasCoverLetter(data)) {
		throw new ORPCError("NOT_FOUND", { message: "No cover letter found for this resume" });
	}

	const filename = generateFilename(target === "cover-letter" ? `${resume.name} Cover Letter` : resume.name, "pdf");

	try {
		// Lazy-load the PDF renderer (@reactive-resume/pdf → @react-pdf/renderer +
		// phosphor-icons-react-pdf, ~10.6k icon modules) only when a PDF is actually
		// exported, instead of at server boot. Slashes cold-start file I/O on
		// constrained/slow-disk hosts. See fork perf/lazy-load-pdf.
		const { createResumePdfFile } = await import("@reactive-resume/pdf/server");
		const body = await createResumePdfFile({ data: getResumeExportData(data, target), filename });

		return {
			headers: {
				"content-disposition": `attachment; filename="${filename}"`,
			},
			body,
		};

View on GitHub (pinned to 3a5b12e2a4)