fix: report document load failures instead of deferring them as -1 page count - #301
Merged
PeterStaar-IBM merged 1 commit intoJul 19, 2026
Merged
Conversation
…ge count When qpdf cannot parse a document, pdf_decoder<DOCUMENT> correctly returns false with number_of_pages left at its -1 sentinel - but every pybind loader discarded that return value and reported success. The failure only surfaced downstream: is_loaded() true, number_of_pages() == -1, docling logging 'Inconsistent number of pages: N!=-1' and rejecting the document (docling-project#239, docling#3031). The threaded parser was quieter still, silently scheduling zero pages from the -1 count. All four loaders now check the decoder result, erase the never-parsed decoder from the registry, and return false, so the existing Python-side RuntimeError fires at load time. normalise_page_numbers additionally rejects negative page counts outright. Documents that qpdf can parse load exactly as before. Fixes docling-project#239 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Jeff Witt <152964771+witt3rd@users.noreply.github.com>
Contributor
|
✅ DCO Check Passed Thanks @wittjeff, all your commits are properly signed off. 🎉 |
Contributor
Merge Protections🟢 Merge protection satisfied — ready to merge. Show 1 satisfied protection🟢 Enforce conventional commitMake sure that we follow https://www.conventionalcommits.org/en/v1.0.0/
|
Member
|
@cau-git can you have a quick look: I think it is a good solution, but need a second pair of eyes. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #239. Related: docling-project/docling#3031.
Problem
When qpdf cannot parse a document,
pdf_decoder<DOCUMENT>::process_document_from_bytesiocorrectly catches the exception, logs it, and returnsfalse— withnumber_of_pagesstill at its-1sentinel. But every pybind loader discards that return value:docling_parser::load_documentreturnstruewhenever the file merely exists;docling_parser::load_document_from_bytesioreturnstrueunless an exception escapes — which it never does, because the decoder catches internally;docling_threaded_baseloaders have the same ignored return (their try/catch is equally unreachable for this failure).So the Python
load()— which does check the loader result and would raise — never gets the signal. The failure only surfaces later and indirectly:Downstream, docling propagates the
-1asInconsistent number of pages: N!=-1and rejects the document viais_valid()(docling#3031). In the threaded parser it is quieter still:normalise_page_numbers(key, -1, None)silently produces an empty schedule — the corrupt document "loads" and processes zero pages with no error at all, and with an explicit page list the user gets the bafflingInvalid page number 1 ... with -1 pages.#232 (page count from
getAllPages()) and #240 robustify counting for documents qpdf can open; this PR closes the remaining path, where qpdf cannot open the document at all.Fix
All four loaders now check the decoder's result; on failure they erase the just-created decoder from the registry and return
false:is_loaded()reportingtruewithnumber_of_pages() == -1);raise RuntimeError("Failed to load document with key ...")now fires at load time — a loud, immediate failure exactly where the caller can act on it (e.g. docling falling back to its lenient pypdfium2 backend, as discussed in docling#3031);normalise_page_numbersadditionally rejects a negative page count outright, so a-1can never silently schedule an empty task list.This is signalling-only: documents that qpdf can parse (including with warnings) load exactly as before. No groundtruth impact.
Tests
tests/test_load_failure.py: garbage bytes via BytesIO and via file path raiseRuntimeErroratload()for bothDoclingPdfParserandDoclingThreadedPdfParser; a failed load leaves no stale key registered; minimal valid PDFs still load with the correct page count.