mozilla/pdf.js · critical · InvalidPDFException
Invalid PDF structure.
Error message
Invalid PDF structure.
What it means
Thrown as an InvalidPDFException after pdf.js exhausts every recovery path while trying to find the document's trailer/Catalog. The loop in XRef#findTrailerDict scans all parsed xref entries for any dictionary containing a /Root key; if none qualifies, the PDF has no discoverable Catalog and cannot be opened. This is the terminal failure for documents whose cross-reference tables and trailers are unrecoverably damaged.
Source
Thrown at src/core/xref.js:729
const ref = Ref.get(parseInt(num, 10), entry.gen);
let obj;
try {
obj = this.fetch(ref);
} catch {
continue;
}
if (obj instanceof BaseStream) {
obj = obj.dict;
}
if (obj instanceof Dict && obj.has("Root")) {
return obj;
}
}
}
// nothing helps
throw new InvalidPDFException("Invalid PDF structure.");
}
readXRef(recoveryMode = false) {
const stream = this.stream;
// Keep track of already parsed XRef tables, to prevent an infinite loop
// when parsing corrupt PDF files where e.g. the /Prev entries create a
// circular dependency between tables (fixes bug1393476.pdf).
const startXRefParsedCache = new Set();
while (this.startXRefQueue.length) {
try {
const startXRef = this.startXRefQueue[0];
if (startXRefParsedCache.has(startXRef)) {
warn("readXRef - skipping XRef table since it was already parsed.");
this.startXRefQueue.shift();
continue;
}View on GitHub (pinned to 5903d58d58)
Solutions
- Open the file in a desktop PDF reader to confirm it is actually a valid PDF; if other readers also fail, the file is corrupt and cannot be processed.
- Verify the byte stream you pass to getDocument is the raw PDF bytes (check it starts with %PDF- and ends with %%EOF) and not a wrapped/error response.
- Re-fetch or re-export the source PDF from its origin to obtain a non-truncated copy.
- If you control generation, regenerate the PDF (the writer emitted a broken xref/trailer).
- As a last resort, attempt repair with an external tool (qpdf --check, ghostscript) then re-load.
Example fix
// before
const task = pdfjsLib.getDocument({ url });
const doc = await task.promise; // throws InvalidPDFException
// after
const buf = await (await fetch(url)).arrayBuffer();
const bytes = new Uint8Array(buf);
const head = String.fromCharCode(...bytes.subarray(0, 5));
if (head !== '%PDF-') {
throw new Error('File is not a PDF (missing %PDF- header)');
}
const task = pdfjsLib.getDocument({ data: bytes });
try {
const doc = await task.promise;
} catch (e) {
if (e instanceof pdfjsLib.InvalidPDFException) {
// surface a user-friendly 'corrupt file' message
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Validate the bytes look like a PDF before loading.
function looksLikePdf(bytes) {
const head = String.fromCharCode(...bytes.subarray(0, 5));
const tail = String.fromCharCode(...bytes.subarray(bytes.length - 6));
return head === '%PDF-' && tail.includes('%%EOF');
}
const bytes = new Uint8Array(await (await fetch(url)).arrayBuffer());
if (!looksLikePdf(bytes)) throw new Error('Not a valid PDF file'); Try / catch
try {
const doc = await loadingTask.promise;
} catch (e) {
if (e instanceof pdfjsLib.InvalidPDFException) {
// show user 'This file is not a valid PDF' and stop.
return null;
}
throw e;
} Prevention
- Reject uploads that do not start with %PDF- at the form/API layer.
- Compare downloaded Content-Length to the source to catch truncation.
- Never assume an HTTP error body is a PDF even with a .pdf URL.
When it happens
Trigger: A PDF whose xref table, xref stream, and trailer are all missing or unreadable AND no object in the file can be fetched as a dictionary containing /Root. Typically reached when startxRef points nowhere, the trailer lacks /Root, and the fallback scan in XRef fails. Also hit when the file is not actually a PDF (e.g. HTML/JSON served with wrong MIME) yet partially tokenized.
Common situations: Serving a truncated or zero-byte download; a download that saved an HTML error page with a .pdf extension; PDFs corrupted by transfer/storage; encrypted-with-broken-trailer files; user-uploaded content that failed validation upstream.
Related errors
- Invalid Root reference.
- Catalog object is not a dictionary.
- Page dictionary kid reference points to wrong type of object
- Node must be a dictionary.
- bad ObjStm stream
AI-assisted analysis of mozilla/pdf.js@5903d58d58 (2026-08-13).
Data as JSON: /api/errors/ed3435d37b9414aa.
Report an issue: GitHub.