{"record":{"id":"f61a0d721293f7bb","repo":"mozilla/pdf.js","slug":"kid-node-must-be-a-dictionary","errorCode":null,"errorMessage":"Kid node must be a dictionary.","messagePattern":"Kid node must be a dictionary\\.","errorType":"exception","errorClass":"FormatError","httpStatus":null,"severity":"error","filePath":"src/core/catalog.js","lineNumber":1651,"sourceCode":"      }\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\")) {\n              const count = obj.get(\"Count\");\n              if (Number.isInteger(count) && count >= 0) {\n                total += count;\n                return;\n              }\n              throw new FormatError(\"Count must be a (positive) integer.\");\n            }\n            // Page leaf node.\n            total++;\n          })\n        );\n      }\n      if (!found) {\n        throw new FormatError(\"Kid reference not found in parent's kids.\");\n      }\n      await Promise.all(kidPromises);","sourceCodeStart":1633,"sourceCodeEnd":1669,"githubUrl":"https://github.com/mozilla/pdf.js/blob/5903d58d58e4dd9ce6ffa3834aea8480f06b4ada/src/core/catalog.js#L1633-L1669","documentation":"Thrown inside getPageIndex's kid-counting loop after fetching a /Kids reference via xref.fetchAsync(kid). The resolved object is not a dictionary, but page and intermediate nodes must be dictionaries. This indicates a dangling or mispointed reference in the page tree.","triggerScenarios":"A /Kids entry references an object that the xref resolves to a non-Dict (e.g., a stream, string, null, or number). The check runs inside `xref.fetchAsync(kid).then(obj => { if (!(obj instanceof Dict)) throw ... })`.","commonSituations":"Broken xref tables pointing /Kids entries at the wrong objects; PDFs with free or reused object numbers; files corrupted after generation; output from tools that write refs to non-page objects.","solutions":["Catch the rejection from getPageIndex and fall back to linear getPage iteration.","Run the file through qpdf --fix or Ghostscript to rebuild the xref and page tree.","If generated in-house, validate object graph integrity before writing the xref."],"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":["Rebuild xref tables with qpdf --fix to repair dangling /Kids references.","Validate object-graph integrity in your PDF generation pipeline.","Keep a linear-scan fallback for documents with suspect xref tables."],"tags":["pdf","catalog","page-tree","corrupt-pdf","xref"],"backgroundTag":null,"analyzedSha":"5903d58d58e4dd9ce6ffa3834aea8480f06b4ada","analyzedAt":"2026-08-13T02:28:27.364Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}