{"record":{"id":"1b123ccd9e03e8ab","repo":"mozilla/pdf.js","slug":"kids-must-be-an-array","errorCode":null,"errorMessage":"Kids must be an array.","messagePattern":"Kids must be an array\\.","errorType":"exception","errorClass":"FormatError","httpStatus":null,"severity":"error","filePath":"src/core/catalog.js","lineNumber":1635,"sourceCode":"          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;\n          break;\n        }\n        kidPromises.push(\n          xref.fetchAsync(kid).then(obj => {\n            if (!(obj instanceof Dict)) {\n              throw new FormatError(\"Kid node must be a dictionary.\");\n            }\n            if (obj.has(\"Count\")) {","sourceCodeStart":1617,"sourceCodeEnd":1653,"githubUrl":"https://github.com/mozilla/pdf.js/blob/5903d58d58e4dd9ce6ffa3834aea8480f06b4ada/src/core/catalog.js#L1617-L1653","documentation":"Thrown by Catalog.getPageIndex() after resolving a node's /Parent to a dictionary. That parent's /Kids entry is present but is not a PDF array. The spec mandates /Kids be an array of indirect references to child page or intermediate nodes, so a non-array value means the tree is structurally invalid.","triggerScenarios":"Calling getPageIndex on a PDF whose parent /Pages node has /Kids set to a single dictionary, a reference, a number, or any non-array object. The check is `Array.isArray(await parent.getAsync('Kids'))` returning false.","commonSituations":"Generators that emit a single child directly in /Kids instead of a one-element array; corrupt or partially rewritten page trees; PDFs merged incorrectly so /Kids arrays were flattened; spec-violating producers common in older office exporters.","solutions":["Fall back to a linear page scan (iterate pdfDocument.getPage(i)) wrapped in try-catch around getPageIndex.","Rebuild the page tree with qpdf or Ghostscript to normalize /Kids into a proper array.","Regenerate the PDF from the source document with a compliant producer."],"exampleFix":"// before\nconst idx = await pdfDocument.getPageIndex(ref);\n\n// after\nlet idx = -1;\ntry {\n  idx = await pdfDocument.getPageIndex(ref);\n} catch (e) {\n  for (let i = 0; i < pdfDocument.numPages; i++) {\n    const p = await pdfDocument.getPage(i);\n    if (p.ref && p.ref.num === ref.num) { idx = i; break; }\n  }\n}","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"try {\n  idx = await pdfDocument.getPageIndex(ref);\n} catch (e) {\n  for (let i = 0; i < pdfDocument.numPages; i++) {\n    const p = await pdfDocument.getPage(i);\n    if (p.ref && p.ref.num === ref.num) { idx = i; break; }\n  }\n}","preventionTips":["Repair source PDFs with qpdf/Ghostscript to normalize /Kids arrays.","When generating PDFs, always emit /Kids as an array of indirect references.","Wrap getPageIndex in try-catch with a linear-scan fallback for untrusted inputs."],"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"}