Skip to main content

Errors & debugging

The error taxonomy, template attribution on failure, and the diagnostics the development preview surfaces.

Render failures are typed and attributed. Every error surfaced from a template's render() is a NuxtPdfError carrying a machine-readable code and the templateKey. Development renders also carry templateFile and prefix the message with the source path; production deliberately omits that preview-only source attribution.

The error taxonomy

CodeMeaning
PDF_TEMPLATE_NOT_FOUNDNo template matches the requested name
PDF_TEMPLATE_INVALIDThe template misuses the API. For example, it calls usePdfPageNumbers() outside a PDF render.
PDF_TREE_INVALIDInvalid root/nesting, SVG references, or props outside a primitive's closed runtime surface
PDF_LAYOUT_ERRORThe layout engine rejected the tree. This error includes font resolution failures ("Font family not registered").
PDF_LIMIT_EXCEEDEDThe multi-pass loop did not converge within maxPasses
PDF_ASSET_INVALIDA local asset failed extension, signature, size, or containment validation
PDF_ASSET_BLOCKEDA remote asset was refused by the allowlist, timed out, exceeded its cap, or was otherwise blocked
PDF_RENDER_ERRORA render failure that does not fall into a more specific category

Font-resolution failures deliberately surface as a single PDF_LAYOUT_ERROR (font resolution is a layout sub-stage) and preserve React PDF's exact "Font family not registered" text.

Template attribution

NuxtPdfError is thrown with the failing template's identity attached, so you can log or branch on it precisely:

server/api/report.get.ts
import { NuxtPdfError, pdf } from '#pdf'

export default defineEventHandler(async () => {
  try {
    const result = await pdf.report.render(props)
    return result.response()
  }
  catch (error) {
    if (error instanceof NuxtPdfError) {
      console.error(error.code, error.templateKey, error.templateFile)
    }
    throw error
  }
})

The message itself is always prefixed with the template name. In development it also includes the source file in pdfs/, so an unhandled preview error points straight to the authoring file. Invalid nesting, unknown or misplaced props, DOM/event attributes, and invalid SVG references fail with PDF_TREE_INVALID; Nuxt PDF does not return a partially pruned document or silently forward unsupported input to the engine.

Production render diagnostics

Every successful PdfRenderResult exposes frozen metadata for that completed render. Its title and language reflect the PDF Info fields; filename is response/download metadata and is not embedded as PDF Info. The result also exposes the same immutable diagnostics object shown by the preview: duration, byte length, page count, layout passes, and registered font faces. Diagnostics contain no document content, props, or resource URLs, so an application can forward them to its own metrics layer.

The development preview as a debugger

/_pdf is where you live while building a document. Each viewer page:

  • offers the sampleData and every named scenario as tabs that swap the embedded PDF (an unknown ?scenario= 404s with the available names);
  • calls the public template's render(props) once and embeds that exact completed result. There is no preview-specific rendering path;
  • shows the result's same diagnostics object, including duration and output size, page count, and the number of layout passes; and
  • replaces the document with a safe error panel that shows the code, template, and relative source attribution, and a content-free summary, with no stack, props, customer text, URL, or absolute path. If that scenario rendered successfully before, the previous PDF stays visible with an explicit stale marker.

The layout-pass count is a useful debugging signal: a plain document reports one pass; a table of contents reports two; a count that climbs toward maxPasses means the document is struggling to converge (see Contents, links & bookmarks).

A development-only Vite event reloads the viewer after PDF SFC changes and the scenario query remains active. Refresh, raw-inline, and download actions remain available explicitly.

The preview fixture sidecar and its source paths exist only in development. Production compilation omits sampleData and scenarios from the template module itself, and an artifact test rejects their canary values or preview-only API tokens anywhere in the emitted Nitro server bundle.

Common failures

  • #pdf is untyped or /_pdf returns 404 right after you enable the module. restart nuxt dev or run nuxt prepare so Nuxt writes the registry.
  • PDF_LAYOUT_ERROR: Font family not registered means that the fontFamily in a style is not declared in pdf.fonts, or the face (weight/style) is missing.
  • PDF_ASSET_INVALID means that the image or font path escapes its root, has the wrong signature, or exceeds its size cap.
  • PDF_LIMIT_EXCEEDED means that the document's layout depends on the page numbers it prints, so it never converges; it is not made to converge (see the note on non-convergence in Contents, links & bookmarks).