mozilla/pdf.js · critical · FormatError

Catalog object is not a dictionary.

Error message

Catalog object is not a dictionary.

What it means

Thrown in the Catalog constructor (catalog.js:157) when the root/catalog object returned by xref.getCatalogObj() is not a PDF Dict. The catalog is the document's top-level dictionary (/Root); if it is anything else (null, stream, array, number), the PDF is structurally invalid and cannot be processed at all.

Source

Thrown at src/core/catalog.js:157

  nonBlendModesSet = new RefSet();

  pageDictCache = new RefMap();

  pageIndexCache = new RefMap();

  pageKidsCountCache = new RefMap();

  standardFontDataCache = new Map();

  systemFontCache = new Map();

  constructor(pdfManager, xref) {
    this.pdfManager = pdfManager;
    this.xref = xref;

    this.#catDict = xref.getCatalogObj();
    if (!(this.#catDict instanceof Dict)) {
      throw new FormatError("Catalog object is not a dictionary.");
    }
    // Given that `XRef.parse` will both fetch *and* validate the /Pages-entry,
    // the following call must always succeed here:
    this.toplevelPagesDict; // eslint-disable-line no-unused-expressions
  }

  cloneDict() {
    return this.#catDict.clone();
  }

  /**
   * Create an id for an attachment from a FileAttachment annotation.
   *
   * The id is registered here rather than parsed from a public string prefix in
   * `attachmentContent`, since catalog attachment names can be arbitrary PDF
   * strings and may otherwise collide with annotation-local ids.
   *
   * @param {Ref} ref

View on GitHub (pinned to 5903d58d58)

Solutions

  1. Re-acquire the PDF from a trusted source and confirm it opens in Adobe Acrobat or another strict reader.
  2. Run a structural check: 'qpdf --check file.pdf' or 'mutool info file.pdf' to inspect the trailer and /Root.
  3. Repair the xref/catalog with 'qpdf --linearize' or 'mutool clean' and load the repaired output.
  4. Verify the byte stream passed to getDocument() is the complete file (check size/checksum against the source).

Example fix

// before: passing a possibly-truncated/corrupt blob unchecked
const task = getDocument({ data: arrayBuffer });

// after: guard with a quick structural sanity check + repair fallback
function looksLikePdf(buf) {
  const head = new TextDecoder().decode(buf.slice(0, 5));
  return head === '%PDF-';
}
if (!looksLikePdf(arrayBuffer)) throw new Error('not a PDF');
try {
  await getDocument({ data: arrayBuffer }).promise;
} catch (e) {
  // route the user to a server-side repair (qpdf/ghostscript) then retry
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Best-effort pre-check: confirm the bytes look like a PDF before loading.
function isLikelyPdf(buf) {
  if (!(buf instanceof ArrayBuffer) || buf.byteLength < 8) return false;
  return new TextDecoder().decode(new Uint8Array(buf, 0, 5)) === '%PDF-';
}
if (!isLikelyPdf(arrayBuffer)) {
  throw new Error('Input is not a recognizable PDF; refusing to load.');
}
// Structural repair should be done server-side (qpdf/mutool) before retry.

Try / catch

try {
  const pdf = await getDocument({ data: arrayBuffer }).promise;
} catch (err) {
  if (/Catalog object is not a dictionary/i.test(err?.message)) {
    // Root/catalog is unusable; route user to repair/regenerate the file.
    throw new Error('This PDF is too corrupt to open (bad catalog). Please re-export it.', { cause: err });
  }
  throw err;
}

Prevention

When it happens

Trigger: getDocument() loads a PDF whose trailer /Root indirect reference resolves to a non-dictionary object, or whose xref table is corrupt so the catalog ref points at the wrong object. The Catalog constructor runs during initial document setup, so this fails the whole load.

Common situations: Truncated or partially-written PDF file; corrupt xref/cross-reference stream pointing /Root at garbage; a non-PDF file (or a PDF header glued to other data) handed to getDocument(); a file that was repaired badly by a tool that lost the catalog.

Related errors


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