{"record":{"id":"848ec3f2031c55c9","repo":"mozilla/pdf.js","slug":"parent-must-be-a-dictionary","errorCode":null,"errorMessage":"Parent must be a dictionary.","messagePattern":"Parent must be a dictionary\\.","errorType":"exception","errorClass":"FormatError","httpStatus":null,"severity":"error","filePath":"src/core/catalog.js","lineNumber":1627,"sourceCode":"        break;\n      }\n      if (!(node instanceof Dict)) {\n        throw new FormatError(\"Node must be a dictionary.\");\n      }\n      const parentRef = node.getRaw(\"Parent\");\n      if (parentRef instanceof Ref) {\n        if (visited.has(parentRef)) {\n          throw new FormatError(\"Pages tree contains circular reference.\");\n        }\n        visited.put(parentRef);\n      }\n\n      const parent = await node.getAsync(\"Parent\");\n      if (!parent) {\n        break;\n      }\n      if (!(parent instanceof Dict)) {\n        throw new FormatError(\"Parent must be a dictionary.\");\n      }\n\n      const kids = await parent.getAsync(\"Kids\");\n      if (!kids) {\n        break;\n      }\n      if (!Array.isArray(kids)) {\n        throw new FormatError(\"Kids must be an array.\");\n      }\n\n      const kidPromises = [];\n      let found = false;\n      for (const kid of kids) {\n        if (!(kid instanceof Ref)) {\n          throw new FormatError(\"Kid must be a reference.\");\n        }\n        if (isRefsEqual(kid, ref)) {\n          found = true;","sourceCodeStart":1609,"sourceCodeEnd":1645,"githubUrl":"https://github.com/mozilla/pdf.js/blob/5903d58d58e4dd9ce6ffa3834aea8480f06b4ada/src/core/catalog.js#L1609-L1645","documentation":"Thrown by Catalog.getPageIndex() while walking the /Pages tree upward from a page reference. The node's /Parent entry exists but does not resolve to a PDF dictionary (it is some other object type). The PDF specification requires every page and intermediate node's /Parent to be a dictionary, so this signals a malformed page tree.","triggerScenarios":"Calling pdfDocument.getPageIndex(pageRef) on a document where a page or intermediate /Pages node stores a non-dictionary value (string, number, name, array, or stream) in its /Parent entry. The check is `node.getAsync('Parent')` followed by `parent instanceof Dict`.","commonSituations":"PDFs produced by buggy generators that emit /Parent as a direct value rather than an indirect reference; files truncated or byte-corrupted in transfer; hand-edited or stitched-together PDFs whose page tree was not rebuilt; PDFs repaired by naive fix-up tools that left dangling /Parent pointers.","solutions":["Wrap getPageIndex in try-catch and fall back to a linear search: iterate pdfDocument.getPage(i) until the matching ref is found.","Repair the source PDF with qpdf --check/--fix or Ghostscript (gs -sDEVICE=pdfwrite) to rebuild a valid page tree.","If the file is from your own pipeline, regenerate it with a spec-compliant library so /Parent is always an indirect dictionary reference."],"exampleFix":"// before\nconst pageIndex = await pdfDocument.getPageIndex(pageRef);\n\n// after\nlet pageIndex;\ntry {\n  pageIndex = await pdfDocument.getPageIndex(pageRef);\n} catch (e) {\n  // Fallback: linear scan when the page tree is malformed.\n  for (let i = 0; i < pdfDocument.numPages; i++) {\n    const page = await pdfDocument.getPage(i);\n    if (page.ref && page.ref.num === pageRef.num) { pageIndex = i; break; }\n  }\n}","handlingStrategy":"try-catch","validationCode":"// Before relying on getPageIndex, ensure the page tree is well-formed\n// by attempting a lightweight parent check (best-effort; full validation\n// requires the worker). In practice, just call getPageIndex defensively.\nasync function safeGetPageIndex(pdfDoc, pageRef) {\n  try {\n    return await pdfDoc.getPageIndex(pageRef);\n  } catch (e) {\n    return -1; // signal caller to fall back\n  }\n}","typeGuard":"null","tryCatchPattern":"try {\n  pageIndex = await pdfDocument.getPageIndex(pageRef);\n} catch (e) {\n  // Page tree is malformed; fall back to linear scan.\n  for (let i = 0; i < pdfDocument.numPages; i++) {\n    const page = await pdfDocument.getPage(i);\n    if (page.ref && page.ref.num === pageRef.num) { pageIndex = i; break; }\n  }\n}","preventionTips":["Validate generated PDFs with qpdf --check before serving them.","Do not hand-edit page tree nodes without rebuilding /Parent and /Kids consistently.","Prefer linear getPage iteration if you only need a few indices and cannot trust the source PDF."],"tags":["pdf","catalog","page-tree","corrupt-pdf","getpageindex"],"backgroundTag":null,"analyzedSha":"5903d58d58e4dd9ce6ffa3834aea8480f06b4ada","analyzedAt":"2026-08-13T02:28:27.364Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}