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
- 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).
- 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.
- Repair the PDF with a tool like qpdf --check / mutool clean / ghostscript to rebuild the xref and catalog, then load the repaired copy.
- 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.
- 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
- Validate the '%PDF-' header and a trailing '%%EOF' before loading.
- Verify the uploaded file's byte length matches the source to catch truncation.
- Run 'qpdf --check' or 'mutool clean' on suspect files to rebuild the xref/catalog before loading.
- Always handle InvalidPDFException separately from network/password errors so users get an accurate message.
- If you generate PDFs, ensure the writer emits a valid trailer with /Root pointing to a catalog Dict that has a valid /Pages tree.
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
- Kid node must be a dictionary.
- Invalid PDF structure.
- Catalog object is not a dictionary.
- Parent must be a dictionary.
- Kids must be an array.
AI-assisted analysis of mozilla/pdf.js@5903d58d58 (2026-08-13).
Data as JSON: /api/errors/2e708fcb447fccb1.
Report an issue: GitHub.