amruthpillai/reactive-resume · error · ORPCError

INTERNAL_SERVER_ERROR

INTERNAL_SERVER_ERROR

Error message

Failed to generate resume PDF

What it means

Catch-all around the lazy-loaded PDF renderer in createResumePdfDownload(). Any error from the dynamic import('@reactive-resume/pdf/server') or createResumePdfFile() is logged with the resumeId and re-thrown as INTERNAL_SERVER_ERROR. The NOT_FOUND at line 28 is thrown before the try block, so it propagates unchanged. The original error is only in server logs, not the response.

Solutions

  1. Inspect server logs for the '[PDF API] Failed to render resume PDF' line — it carries the underlying error.
  2. Validate image URLs and font paths in the resume data before exporting.
  3. Reproduce locally by calling createResumePdfFile({data, filename}) directly to get the raw error.
  4. Increase container memory if rendering large or multi-page resumes.
Defensive patterns

Strategy: try-catch

Validate before calling

function validateResumeAssets(data) {
  const urls = [];
  for (const url of urls) { try { new URL(url); } catch { throw new Error(`Bad image URL: ${url}`); } }
}

Try / catch

try {
  await createResumePdfDownload({ id, userId });
} catch (e) {
  if (e.code === 'INTERNAL_SERVER_ERROR') {
    // surface a generic 'export failed' message; the cause is in server logs
    console.error('PDF export failed for', id);
  } else throw e;
}

Prevention

When it happens

Trigger: Font registration failure, a React-PDF render error (malformed node, unreachable image URL), out-of-memory on large resumes, a broken template, or the pdf package failing to load.

Common situations: A resume referencing a remote image that 404s, a corrupted font file, a template bug after a package upgrade, or insufficient container memory.

Related errors


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

Appendix: source

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

	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,
		};
	} catch (error) {
		console.error("[PDF API] Failed to render resume PDF", { resumeId: input.id, error });
		throw new ORPCError("INTERNAL_SERVER_ERROR", { message: "Failed to generate resume PDF" });
	}
}

export const downloadResumePdfProcedure = protectedProcedure
	.route({
		method: "GET",
		path: "/resumes/{id}/pdf",
		tags: ["Resumes"],
		operationId: "downloadResumePdf",
		summary: "Download resume as PDF",
		description:
			"Generates a PDF for the specified resume and returns it as a forced download. Only resumes belonging to the authenticated user can be downloaded. Requires authentication.",
		successDescription: "The generated resume PDF.",
		outputStructure: "detailed",
	})
	.input(
		z.object({
			id: z.string().describe("The ID of the resume."),

View on GitHub (pinned to 3a5b12e2a4)