{"record":{"id":"3b6ff54e95facd14","repo":"mozilla/pdf.js","slug":"invalid-top-level-pages-dictionary","errorCode":null,"errorMessage":"Invalid top-level pages dictionary.","messagePattern":"Invalid top-level pages dictionary\\.","errorType":"exception","errorClass":"FormatError","httpStatus":null,"severity":"critical","filePath":"src/core/catalog.js","lineNumber":374,"sourceCode":"        throw ex;\n      }\n      warn(\"Unable read to structTreeRoot info.\");\n    }\n    return shadow(this, \"structTreeRoot\", structTree);\n  }\n\n  #readStructTreeRoot() {\n    const rawObj = this.#catDict.getRaw(\"StructTreeRoot\"),\n      obj = this.xref.fetchIfRef(rawObj);\n    return obj instanceof Dict\n      ? new StructTreeRoot(this.xref, obj, rawObj)\n      : null;\n  }\n\n  get toplevelPagesDict() {\n    const pagesObj = this.#catDict.get(\"Pages\");\n    if (!(pagesObj instanceof Dict)) {\n      throw new FormatError(\"Invalid top-level pages dictionary.\");\n    }\n    return shadow(this, \"toplevelPagesDict\", pagesObj);\n  }\n\n  get documentOutline() {\n    let obj = null;\n    try {\n      obj = this.#readDocumentOutline();\n    } catch (ex) {\n      if (ex instanceof MissingDataException) {\n        throw ex;\n      }\n      warn(\"Unable to read document outline.\");\n    }\n    return shadow(this, \"documentOutline\", obj);\n  }\n\n  #readDocumentOutline(options = {}) {","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/mozilla/pdf.js/blob/5903d58d58e4dd9ce6ffa3834aea8480f06b4ada/src/core/catalog.js#L356-L392","documentation":"Thrown by the toplevelPagesDict getter (catalog.js:374) when the catalog's /Pages entry is not a Dict. /Pages is the root node of the page tree; a non-dictionary value means the page tree is unusable. This getter is invoked eagerly in the Catalog constructor, so it aborts document loading.","triggerScenarios":"During Catalog construction, this.#catDict.get('Pages') returns a non-Dict (null, a wrong object, or a ref that resolves to a non-dictionary). Caused by a malformed /Pages entry or a corrupt xref resolving the ref to the wrong object. Note the comment says XRef.parse is expected to have validated this, so reaching the throw indicates that pre-validation was bypassed or the object changed.","commonSituations":"Corrupt PDF where /Pages points at a stream or scalar; xref shifted so /Pages resolves to a different object; a hand-crafted/edited PDF with an inline non-dict Pages value; file truncated after the catalog but before a valid page tree.","solutions":["Repair the PDF: 'mutool clean in.pdf out.pdf' or 'qpdf --linearize in.pdf out.pdf' rebuilds the page tree references.","Confirm the file is complete (not truncated mid-download) by checking its size and re-fetching.","Open in a strict reader to confirm it is actually broken (not a PDF.js-specific parsing gap), then report/file the producer bug.","As a last resort, regenerate the PDF from the original source/document."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// No public API to inspect /Pages before construction; validate input integrity.\nfunction assertCompletePdf(buf) {\n  const u8 = new Uint8Array(buf);\n  const tail = new TextDecoder().decode(u8.subarray(Math.max(0, u8.length - 1024)));\n  if (!/%%EOF/.test(tail)) throw new Error('PDF missing %%EOF — likely truncated');\n}\nawait assertCompletePdf(arrayBuffer);","typeGuard":null,"tryCatchPattern":"try {\n  const pdf = await getDocument({ data: arrayBuffer }).promise;\n} catch (err) {\n  if (/top-level pages dictionary/i.test(err?.message)) {\n    throw new Error('PDF page tree root is invalid; repair with qpdf/mutool.', { cause: err });\n  }\n  throw err;\n}","preventionTips":["Ensure the file is complete (ends with %%EOF) before loading.","Repair the page tree with 'mutool clean' or 'qpdf --linearize'.","Re-download truncated files from the source.","Run 'qpdf --check' on user-supplied PDFs before processing."],"tags":["pdf-structure","catalog","pages-tree","corrupt-pdf","document-load"],"backgroundTag":null,"analyzedSha":"5903d58d58e4dd9ce6ffa3834aea8480f06b4ada","analyzedAt":"2026-08-13T02:28:27.364Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}