{"record":{"id":"ed3435d37b9414aa","repo":"mozilla/pdf.js","slug":"invalid-pdf-structure","errorCode":null,"errorMessage":"Invalid PDF structure.","messagePattern":"Invalid PDF structure\\.","errorType":"exception","errorClass":"InvalidPDFException","httpStatus":null,"severity":"critical","filePath":"src/core/xref.js","lineNumber":729,"sourceCode":"        const ref = Ref.get(parseInt(num, 10), entry.gen);\n        let obj;\n\n        try {\n          obj = this.fetch(ref);\n        } catch {\n          continue;\n        }\n        if (obj instanceof BaseStream) {\n          obj = obj.dict;\n        }\n        if (obj instanceof Dict && obj.has(\"Root\")) {\n          return obj;\n        }\n      }\n    }\n\n    // nothing helps\n    throw new InvalidPDFException(\"Invalid PDF structure.\");\n  }\n\n  readXRef(recoveryMode = false) {\n    const stream = this.stream;\n    // Keep track of already parsed XRef tables, to prevent an infinite loop\n    // when parsing corrupt PDF files where e.g. the /Prev entries create a\n    // circular dependency between tables (fixes bug1393476.pdf).\n    const startXRefParsedCache = new Set();\n\n    while (this.startXRefQueue.length) {\n      try {\n        const startXRef = this.startXRefQueue[0];\n\n        if (startXRefParsedCache.has(startXRef)) {\n          warn(\"readXRef - skipping XRef table since it was already parsed.\");\n          this.startXRefQueue.shift();\n          continue;\n        }","sourceCodeStart":711,"sourceCodeEnd":747,"githubUrl":"https://github.com/mozilla/pdf.js/blob/5903d58d58e4dd9ce6ffa3834aea8480f06b4ada/src/core/xref.js#L711-L747","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst task = pdfjsLib.getDocument({ url });\nconst doc = await task.promise; // throws InvalidPDFException\n\n// after\nconst buf = await (await fetch(url)).arrayBuffer();\nconst bytes = new Uint8Array(buf);\nconst head = String.fromCharCode(...bytes.subarray(0, 5));\nif (head !== '%PDF-') {\n  throw new Error('File is not a PDF (missing %PDF- header)');\n}\nconst task = pdfjsLib.getDocument({ data: bytes });\ntry {\n  const doc = await task.promise;\n} catch (e) {\n  if (e instanceof pdfjsLib.InvalidPDFException) {\n    // surface a user-friendly 'corrupt file' message\n  }\n}","handlingStrategy":"try-catch","validationCode":"// Validate the bytes look like a PDF before loading.\nfunction looksLikePdf(bytes) {\n  const head = String.fromCharCode(...bytes.subarray(0, 5));\n  const tail = String.fromCharCode(...bytes.subarray(bytes.length - 6));\n  return head === '%PDF-' && tail.includes('%%EOF');\n}\n\nconst bytes = new Uint8Array(await (await fetch(url)).arrayBuffer());\nif (!looksLikePdf(bytes)) throw new Error('Not a valid PDF file');","typeGuard":null,"tryCatchPattern":"try {\n  const doc = await loadingTask.promise;\n} catch (e) {\n  if (e instanceof pdfjsLib.InvalidPDFException) {\n    // show user 'This file is not a valid PDF' and stop.\n    return null;\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["pdf-structure","corruption","xref","trailer","invalid-pdf"],"backgroundTag":null,"analyzedSha":"5903d58d58e4dd9ce6ffa3834aea8480f06b4ada","analyzedAt":"2026-08-13T02:28:27.364Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}