mozilla/pdf.js · error · FormatError

Kid node must be a dictionary.

Error message

Kid node must be a dictionary.

What it means

Thrown inside getPageIndex's kid-counting loop after fetching a /Kids reference via xref.fetchAsync(kid). The resolved object is not a dictionary, but page and intermediate nodes must be dictionaries. This indicates a dangling or mispointed reference in the page tree.

Source

Thrown at src/core/catalog.js:1651

      }
      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")) {
              const count = obj.get("Count");
              if (Number.isInteger(count) && count >= 0) {
                total += count;
                return;
              }
              throw new FormatError("Count must be a (positive) integer.");
            }
            // Page leaf node.
            total++;
          })
        );
      }
      if (!found) {
        throw new FormatError("Kid reference not found in parent's kids.");
      }
      await Promise.all(kidPromises);

View on GitHub (pinned to 5903d58d58)

Solutions

  1. Catch the rejection from getPageIndex and fall back to linear getPage iteration.
  2. Run the file through qpdf --fix or Ghostscript to rebuild the xref and page tree.
  3. If generated in-house, validate object graph integrity before writing the xref.

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: A /Kids entry references an object that the xref resolves to a non-Dict (e.g., a stream, string, null, or number). The check runs inside `xref.fetchAsync(kid).then(obj => { if (!(obj instanceof Dict)) throw ... })`.

Common situations: Broken xref tables pointing /Kids entries at the wrong objects; PDFs with free or reused object numbers; files corrupted after generation; output from tools that write refs to non-page objects.

Related errors


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