PDFreact-print-pdf
DocsBrowser-only alpha

Error handling

Typed errors thrown by exportToPDF and registerFont, with stable codes you can branch on.

Edit this page

react-print-pdf throws a custom PdfExportError from every public API failure. The error has a stable code you can switch on without parsing the message.

The PdfExportError shape

import { PdfExportError, isPdfExportError } from "react-print-pdf";

class PdfExportError extends Error {
  readonly code: PdfExportErrorCode;
  readonly context: PdfExportErrorContext;
  // ES2022 standard `cause` field — set when an underlying error
  // (fetch, FontFace.load) was wrapped.
  readonly cause?: unknown;
}

Use isPdfExportError(value) as a type guard inside catch blocks.

Codes

CodeWhereWhy it fires
INVALID_ELEMENTexportToPDFThe element argument was null or undefined. Usually a missing ref.current.
FONT_FETCH_FAILEDregisterFontThe font URL returned a non-2xx HTTP status. Check the URL is reachable and CORS-allowed.
FONT_INVALID_BYTESregisterFontThe response body was shorter than 100 bytes. Almost always means the server sent an HTML 404 page or a redirect, not a font file.
FONT_LOAD_FAILEDregisterFontThe browser's FontFace.load() rejected the bytes. Common cause: the URL points at WOFF/WOFF2, which pdf-lib cannot embed. Use TTF or OTF.
RENDER_DETACHEDinternal (header / footer rerender)The hidden React root was unmounted before rerender was called. Should not happen during a normal export.

Codes are stable across patch releases. New codes may be added in minor releases. Renames are breaking changes.

Image-fetch and raster-fallback failures intentionally do not throw — they print a console.warn and the export continues with the failed asset replaced. Promoting them to errors would break documents that already tolerate lossy export. A future opt-in onError callback in ExportOptions will let you escalate these to thrown errors per-call.

Branching example

import { exportToPDF, isPdfExportError } from "react-print-pdf";

async function handleExport(el: HTMLElement) {
  try {
    const { blob } = await exportToPDF(el, { filename: "report.pdf" });
    download(blob);
  } catch (e) {
    if (isPdfExportError(e)) {
      switch (e.code) {
        case "INVALID_ELEMENT":
          toast.error("The export target hasn't mounted yet — try again in a moment.");
          break;
        case "FONT_FETCH_FAILED":
        case "FONT_INVALID_BYTES":
        case "FONT_LOAD_FAILED":
          // Log structured context for the bug tracker.
          reportToSentry({ code: e.code, ...e.context, cause: e.cause });
          toast.error("Failed to load a custom font — falling back to defaults.");
          break;
        default:
          toast.error("Export failed. The team has been notified.");
          reportToSentry({ code: e.code, message: e.message });
      }
      return;
    }
    // Non-PdfExportError (probably a pdf-lib bug or unhandled DOM error).
    reportToSentry(e);
    toast.error("Unexpected error.");
  }
}

Diagnostic context

error.context carries free-form fields useful for logs and bug reports. They are not part of the public branching contract — fields may appear or disappear between minor releases.

FieldTypeSet by
urlstringFONT_*
statusnumberFONT_FETCH_FAILED
byteLengthnumberFONT_INVALID_BYTES, FONT_LOAD_FAILED
detailstringFONT_LOAD_FAILED (underlying browser error message)

Always log error.code, error.message, error.context, and error.cause together. That tuple is enough to reproduce 95% of the failure modes we've seen in the wild.

On this page