{"record":{"id":"6f759d92eafd71b7","repo":"docling-project/docling","slug":"threadeddoclingparsedocumentbackend-only-supports","errorCode":null,"errorMessage":"ThreadedDoclingParseDocumentBackend only supports iter_pages().","messagePattern":"ThreadedDoclingParseDocumentBackend only supports iter_pages\\(\\)\\.","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"docling/backend/docling_parse_backend.py","lineNumber":555,"sourceCode":"        docling-parse document is loaded purely to read the (cheap, structure-only) outline.\n        \"\"\"\n        password = (\n            self.options.password.get_secret_value() if self.options.password else None\n        )\n        if isinstance(self.path_or_stream, BytesIO):\n            self.path_or_stream.seek(0)\n        dp_doc = DoclingPdfParser(loglevel=\"fatal\").load(\n            path_or_stream=self.path_or_stream, lazy=True, password=password\n        )\n        if dp_doc is None:\n            return []\n        try:\n            return extract_outline_from_docling_parse(dp_doc)\n        finally:\n            dp_doc.unload()\n\n    def load_page(self, page_no: int) -> PdfPageBackend:\n        raise NotImplementedError(\n            \"ThreadedDoclingParseDocumentBackend only supports iter_pages().\"\n        )\n\n    def iter_pages(self) -> Iterator[ThreadedDoclingParsePageBackend]:\n        for result in self.parser.iterate_results():\n            yield ThreadedDoclingParsePageBackend(result)\n\n    def unload(self) -> None:\n        if self._closed:\n            return\n        self._closed = True\n        self.parser.unload(self.doc_key)\n        super().unload()\n","sourceCodeStart":537,"sourceCodeEnd":569,"githubUrl":"https://github.com/docling-project/docling/blob/61d76f1ff3f8428065465889f7b4577da7df704c/docling/backend/docling_parse_backend.py#L537-L569","documentation":"ThreadedDoclingParseDocumentBackend implements the lazily-threaded PDF parsing strategy where pages are produced as the parser completes them via iter_pages(). Its load_page(page_no) intentionally raises NotImplementedError because random-access page loading is incompatible with the threaded, streaming design. Hitting it is an API contract violation, not an environmental problem.","triggerScenarios":"Calling load_page(n) on ThreadedDoclingParseDocumentBackend — directly in custom code, or through generic code that assumes every AbstractPdfDocumentBackend supports random page access (e.g. OCR utilities or page-render helpers that fetch pages by number).","commonSituations":"Custom pipelines written against the non-threaded DoclingParseDocumentBackend (where load_page works) later switched to PdfPipelineMode.PARSE_THREADS; shared helper code iterating pages by index; third-party code unaware of the threaded backend's contract.","solutions":["Use iter_pages() to consume pages from the threaded backend instead of load_page().","If you need random access, switch the pipeline back to the default parse backend (drop the threaded/parse-threads pipeline mode).","Materialize pages first when both patterns are needed: pages = list(backend.iter_pages()), then index into the list.","Guard generic helpers with hasattr/use a capability check or isinstance test against the threaded backend before calling load_page."],"exampleFix":"# before\npage = backend.load_page(3)  # NotImplementedError on threaded backend\n\n# after\npages = list(backend.iter_pages())\npage = pages[3] if len(pages) > 3 else None","handlingStrategy":"validation","validationCode":"# Feature-detect before random page access:\nif hasattr(backend, \"load_page\") and type(backend).load_page is not object.__getattribute__:\n    pass  # too loose; prefer explicit type check below","typeGuard":"from docling.backend.docling_parse_backend import ThreadedDoclingParseDocumentBackend\n\ndef supports_random_page_access(backend) -> bool:\n    return not isinstance(backend, ThreadedDoclingParseDocumentBackend)","tryCatchPattern":"try:\n    page = backend.load_page(n)\nexcept NotImplementedError:\n    pages = list(backend.iter_pages())  # fallback: materialize streaming pages\n    page = pages[n] if n < len(pages) else None","preventionTips":["Write page-consumers against iter_pages() — it works on every backend.","Type-check for the threaded backend before calling load_page in shared helpers.","Materialize pages into a list when both streaming and indexed access are needed."],"tags":["pdf","threaded","api-misuse","not-implemented"],"backgroundTag":null,"analyzedSha":"61d76f1ff3f8428065465889f7b4577da7df704c","analyzedAt":"2026-08-14T23:53:18.727Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}