Repository navigation
feat: report why a document failed to load, so a password is told from a damaged file - #414
Merged
PeterStaar-IBM merged 1 commit intoOct 27, 2026
Conversation
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/
|
PeterStaar-IBM
self-requested a review
October 25, 2026 08:46
Member
|
@wittjeff Can you fix the merge conflict? |
… a damaged file
qpdf says why it refuses a document (an invalid password, a file it
could not repair, ...), but the loaders discarded that and every failure
surfaced as the same RuntimeError("Failed to load document with key ...").
docling's default backend wraps that text unchanged, so an encrypted PDF
was indistinguishable from a corrupt one (docling#4829).
The decoder now records qpdf's error code and message when a load fails;
both native parsers keep the record per key until Python collects it
through take_load_error(); load() raises PdfLoadError with a category
(password, damaged, unsupported, io, unknown) and the qpdf detail, or
PdfPasswordError for an encrypted document, which also tells whether a
password was supplied. Both are RuntimeError subclasses and keep the old
message prefix, so existing handlers keep working.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Jeff Witt <1848307+wittjeff@users.noreply.github.com>
wittjeff
force-pushed
the
fix/load-failure-reason
branch
from
October 25, 2026 19:25
9afcb37 to
9ff5ff0
Compare
Contributor
Author
|
@PeterStaar-IBM Done — rebased onto current The only conflict was in Generated with Claude Code |
PeterStaar-IBM
approved these changes
Oct 26, 2026
wittjeff
added a commit
to wittjeff/docling-parse
that referenced
this pull request
Oct 27, 2026
Resolve the document.h conflict with docling-project#414: keep the load_error fields and the lazy-annotations comment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Jeff Witt <1848307+wittjeff@users.noreply.github.com>
5 tasks done
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.
Parser side of docling#4829 (complements docling#4833).
Problem
qpdf says why it refuses a document:
invalid passwordwith error codeqpdf_e_password,unable to find /Root dictionarywithqpdf_e_damaged_pdf, and so on.pdf_decoder<DOCUMENT>logged that text and returnedfalse, the pybind loaders returnedfalse, and Python raised the sameRuntimeError("Failed to load document with key ...")for every failure. A caller could not tell "needs a password" from "broken file"; docling's default backend wraps that message intoDocumentLoadErrorunchanged, so neither could its users (docling#4829). docling#4833 works around it in docling by scanning the trailer for/Encrypt.Fix
load_error.hmaps the code to a category:password,damaged,unsupported,io,unknown). File-not-found and unreadable files are recorded asio.take_load_error(key)method; a successful load for the key clears it.DoclingPdfParser.loadandDoclingThreadedPdfParser.loadraisePdfLoadError(withcategoryand the qpdfdetail), orPdfPasswordErrorfor an encrypted document, which also tells whether a password was supplied: qpdf reports a missing and a wrong password with the same error, the Python layer knows which it was.Both exceptions are subclasses of
RuntimeErrorand the message keeps the formerFailed to load document with key ...prefix, so existing handlers (docling'sexcept (RuntimeError, ValueError)included) keep working while gaining the reason:docling can later replace the trailer probe of docling#4833 with
isinstance(e, PdfPasswordError)once this is released.Tests
tests/test_unit_load_failure_reason.py, for both parsers: an encrypted document (written with pypdf, astest_unit_page_count.pyalready does) without a password raisesPdfPasswordErrorwithpassword_supplied=False, with a wrong passwordpassword_supplied=True, with the right one it loads; garbage raisesPdfLoadErrorwith categorydamaged, is not a password error, and keeps theRuntimeErrortype and message prefix; a missing file is categoryio; a failed load does not taint a following successful load of the same bytes.tests/test_unit_load_failure.pyfrom #301 passes unchanged.🤖 Generated with Claude Code