mozilla/pdf.js · critical · InvalidPDFException

Invalid Root reference.

Error message

Invalid Root reference.

What it means

Thrown by XRef.parse() (xref.js:184) as an InvalidPDFException when the PDF's cross-reference structure is irrecoverably broken: the trailer's /Root is not a Dict, or its /Pages entry is missing/not a Dict, AND a full-stream recovery pass (recoveryMode) has already been attempted and also failed. This is the terminal failure after worker.js retries parse with recoveryMode=true. The message 'Invalid Root reference.' is the inline literal.

Source

Thrown at src/core/xref.js:184

      try {
        const pages = root.get("Pages");
        if (pages instanceof Dict) {
          this.root = root;
          return;
        }
      } catch (ex) {
        if (ex instanceof MissingDataException) {
          throw ex;
        }
        warn(`XRef.parse - Invalid "Pages" reference: "${ex}".`);
      }
    }

    if (!recoveryMode) {
      throw new XRefParseException();
    }
    // Even recovery failed, there's nothing more we can do here.
    throw new InvalidPDFException("Invalid Root reference.");
  }

  processXRefTable(parser) {
    // Stores state of the table as we process it so we can resume
    // from middle of table in case of missing data error
    this._tableState ??= {
      entryNum: 0,
      streamPos: parser.lexer.stream.pos,
      parserBuf1: parser.buf1,
      parserBuf2: parser.buf2,
    };

    const obj = this.readXRefTable(parser);

    // Sanity check
    if (!isCmd(obj, "trailer")) {
      throw new FormatError(
        "Invalid XRef table: could not find trailer dictionary"

View on GitHub (pinned to 5903d58d58)

Solutions

  1. Confirm the file is a real PDF: check the '%PDF-' header and a '%%EOF' marker, and verify the file size matches the source (re-download if truncated).
  2. Open the file in another viewer (Acrobat/mutool) to confirm it is genuinely corrupt; if it opens there, capture the xref/trailer bytes and file a PDF.js issue with the sample.
  3. Repair the PDF with a tool like qpdf --check / mutool clean / ghostscript to rebuild the xref and catalog, then load the repaired copy.
  4. If you control generation, fix the writer to emit a valid trailer with /Root pointing at a catalog Dict that has a valid /Pages tree.
  5. In your loading code, handle InvalidPDFException and present a clear 'corrupt or unsupported file' message rather than crashing.

Example fix

// before
const doc = await getDocument(data).promise;
// after
import { InvalidPDFException, MissingPDFException } from "pdfjs-dist";
try {
  const doc = await getDocument(data).promise;
} catch (e) {
  if (e instanceof InvalidPDFException) {
    // file is corrupt or not a valid PDF (e.g. broken /Root)
    showError("This file is not a valid PDF and cannot be opened.");
  } else if (e instanceof MissingPDFException) {
    showError("File not found.");
  } else {
    throw e;
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before handing the file to getDocument, sanity-check it is a real,
// non-truncated PDF with an xref/trailer.
async function looksLikeValidPdf(data) {
  const head = new TextDecoder().decode(data.slice(0, 5));
  if (head !== "%PDF-") return false;
  // must end with %%EOF (allow trailing whitespace)
  const tail = new TextDecoder().decode(data.slice(-1024));
  if (!/%%EOF(\s)*$/.test(tail)) return false;
  // size sanity: reject obviously truncated uploads (< a few hundred bytes)
  if (data.byteLength < 200) return false;
  return true;
}

Type guard

// pdfjs-dist exposes exception classes; use them to narrow the catch.
import { InvalidPDFException, MissingPDFException, PasswordException } from "pdfjs-dist";
function isInvalidPdf(e) {
  return e instanceof InvalidPDFException;
}

Try / catch

import { getDocument } from "pdfjs-dist";
import { InvalidPDFException, MissingPDFException } from "pdfjs-dist";
try {
  const loadingTask = getDocument({ data });
  const pdf = await loadingTask.promise;
  // ... use pdf
} catch (e) {
  if (e instanceof InvalidPDFException) {
    // /Root or xref irrecoverably broken (e.g. 'Invalid Root reference.')
    showUserError("This file is corrupt or is not a valid PDF.");
  } else if (e instanceof MissingPDFException) {
    showUserError("File not found.");
  } else if (e instanceof PasswordException) {
    showUserError("A password is required.");
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: PDF.js loads a file, fails to find a valid /Root->/Pages catalog (XRefParseException), then worker.js requests the full loaded stream and retries XRef.parse with recoveryMode=true; recovery still cannot locate a usable catalog, so this exception is thrown. Reached via getDocument()/PDFWorker when opening the file.

Common situations: A truncated or partially downloaded PDF missing the trailer/xref/catalog; a file that is not actually a PDF (wrong magic, HTML/JSON saved as .pdf); a PDF whose /Root or /Pages indirect object is zeroed/corrupted; incremental-update or linearization corruption; a PDF produced by a buggy writer with a broken xref table.

Related errors


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