mozilla/pdf.js · error · FormatError

Parent must be a dictionary.

Error message

Parent must be a dictionary.

What it means

Thrown by Catalog.getPageIndex() while walking the /Pages tree upward from a page reference. The node's /Parent entry exists but does not resolve to a PDF dictionary (it is some other object type). The PDF specification requires every page and intermediate node's /Parent to be a dictionary, so this signals a malformed page tree.

Source

Thrown at src/core/catalog.js:1627

        break;
      }
      if (!(node instanceof Dict)) {
        throw new FormatError("Node must be a dictionary.");
      }
      const parentRef = node.getRaw("Parent");
      if (parentRef instanceof Ref) {
        if (visited.has(parentRef)) {
          throw new FormatError("Pages tree contains circular reference.");
        }
        visited.put(parentRef);
      }

      const parent = await node.getAsync("Parent");
      if (!parent) {
        break;
      }
      if (!(parent instanceof Dict)) {
        throw new FormatError("Parent must be a dictionary.");
      }

      const kids = await parent.getAsync("Kids");
      if (!kids) {
        break;
      }
      if (!Array.isArray(kids)) {
        throw new FormatError("Kids must be an array.");
      }

      const kidPromises = [];
      let found = false;
      for (const kid of kids) {
        if (!(kid instanceof Ref)) {
          throw new FormatError("Kid must be a reference.");
        }
        if (isRefsEqual(kid, ref)) {
          found = true;

View on GitHub (pinned to 5903d58d58)

Solutions

  1. Wrap getPageIndex in try-catch and fall back to a linear search: iterate pdfDocument.getPage(i) until the matching ref is found.
  2. Repair the source PDF with qpdf --check/--fix or Ghostscript (gs -sDEVICE=pdfwrite) to rebuild a valid page tree.
  3. If the file is from your own pipeline, regenerate it with a spec-compliant library so /Parent is always an indirect dictionary reference.

Example fix

// before
const pageIndex = await pdfDocument.getPageIndex(pageRef);

// after
let pageIndex;
try {
  pageIndex = await pdfDocument.getPageIndex(pageRef);
} catch (e) {
  // Fallback: linear scan when the page tree is malformed.
  for (let i = 0; i < pdfDocument.numPages; i++) {
    const page = await pdfDocument.getPage(i);
    if (page.ref && page.ref.num === pageRef.num) { pageIndex = i; break; }
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before relying on getPageIndex, ensure the page tree is well-formed
// by attempting a lightweight parent check (best-effort; full validation
// requires the worker). In practice, just call getPageIndex defensively.
async function safeGetPageIndex(pdfDoc, pageRef) {
  try {
    return await pdfDoc.getPageIndex(pageRef);
  } catch (e) {
    return -1; // signal caller to fall back
  }
}

Type guard

null

Try / catch

try {
  pageIndex = await pdfDocument.getPageIndex(pageRef);
} catch (e) {
  // Page tree is malformed; fall back to linear scan.
  for (let i = 0; i < pdfDocument.numPages; i++) {
    const page = await pdfDocument.getPage(i);
    if (page.ref && page.ref.num === pageRef.num) { pageIndex = i; break; }
  }
}

Prevention

When it happens

Trigger: Calling pdfDocument.getPageIndex(pageRef) on a document where a page or intermediate /Pages node stores a non-dictionary value (string, number, name, array, or stream) in its /Parent entry. The check is `node.getAsync('Parent')` followed by `parent instanceof Dict`.

Common situations: PDFs produced by buggy generators that emit /Parent as a direct value rather than an indirect reference; files truncated or byte-corrupted in transfer; hand-edited or stitched-together PDFs whose page tree was not rebuilt; PDFs repaired by naive fix-up tools that left dangling /Parent pointers.

Related errors


AI-assisted analysis of mozilla/pdf.js@5903d58d58 (2026-08-13). Data as JSON: /api/errors/848ec3f2031c55c9. Report an issue: GitHub.