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
- Wrap getPageIndex in try-catch and fall back to a linear search: iterate pdfDocument.getPage(i) until the matching ref is found.
- Repair the source PDF with qpdf --check/--fix or Ghostscript (gs -sDEVICE=pdfwrite) to rebuild a valid page tree.
- 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
- Validate generated PDFs with qpdf --check before serving them.
- Do not hand-edit page tree nodes without rebuilding /Parent and /Kids consistently.
- Prefer linear getPage iteration if you only need a few indices and cannot trust the source PDF.
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
- Kids must be an array.
- Kid must be a reference.
- Kid node must be a dictionary.
- Count must be a (positive) integer.
- Kid reference not found in parent's kids.
AI-assisted analysis of mozilla/pdf.js@5903d58d58 (2026-08-13).
Data as JSON: /api/errors/848ec3f2031c55c9.
Report an issue: GitHub.