mozilla/pdf.js · error · FormatError

Kids must be an array.

Error message

Kids must be an array.

What it means

Thrown by Catalog.getPageIndex() after resolving a node's /Parent to a dictionary. That parent's /Kids entry is present but is not a PDF array. The spec mandates /Kids be an array of indirect references to child page or intermediate nodes, so a non-array value means the tree is structurally invalid.

Source

Thrown at src/core/catalog.js:1635

          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;
          break;
        }
        kidPromises.push(
          xref.fetchAsync(kid).then(obj => {
            if (!(obj instanceof Dict)) {
              throw new FormatError("Kid node must be a dictionary.");
            }
            if (obj.has("Count")) {

View on GitHub (pinned to 5903d58d58)

Solutions

  1. Fall back to a linear page scan (iterate pdfDocument.getPage(i)) wrapped in try-catch around getPageIndex.
  2. Rebuild the page tree with qpdf or Ghostscript to normalize /Kids into a proper array.
  3. Regenerate the PDF from the source document with a compliant producer.

Example fix

// before
const idx = await pdfDocument.getPageIndex(ref);

// after
let idx = -1;
try {
  idx = await pdfDocument.getPageIndex(ref);
} catch (e) {
  for (let i = 0; i < pdfDocument.numPages; i++) {
    const p = await pdfDocument.getPage(i);
    if (p.ref && p.ref.num === ref.num) { idx = i; break; }
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

null

Try / catch

try {
  idx = await pdfDocument.getPageIndex(ref);
} catch (e) {
  for (let i = 0; i < pdfDocument.numPages; i++) {
    const p = await pdfDocument.getPage(i);
    if (p.ref && p.ref.num === ref.num) { idx = i; break; }
  }
}

Prevention

When it happens

Trigger: Calling getPageIndex on a PDF whose parent /Pages node has /Kids set to a single dictionary, a reference, a number, or any non-array object. The check is `Array.isArray(await parent.getAsync('Kids'))` returning false.

Common situations: Generators that emit a single child directly in /Kids instead of a one-element array; corrupt or partially rewritten page trees; PDFs merged incorrectly so /Kids arrays were flattened; spec-violating producers common in older office exporters.

Related errors


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