You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(parser): password-protected PDFs silently ingest as empty, causing undetectable data loss #242
PyMuPDF (fitz) opens encrypted/password-protected PDFs without raising an exception. Every page's get_text('dict') returns empty blocks. The pipeline continues, produces zero chunks, and upserts nothing into Qdrant — while reporting ingest_status='indexed'. The document is permanently invisible in search with no error to the operator.
Root Cause
ingestion/parser.py around line 133:
doc=fitz.open(stream=data, filetype='pdf') # succeeds silently on encrypted PDF# doc.page_count is non-zero# doc.is_encrypted → True, but never checkedforpageindoc:
yield_parse_page(page) # get_text() returns empty blocks for every page
There is no check for doc.needs_pass or doc.is_encrypted after opening.
Failure Scenario
Tenant uploads a password-protected PDF.
fitz.open() succeeds — encrypted PDFs are valid file structures.
_parse_page() calls page.get_text('dict') on each page → returns {'blocks': []}.
All pages produce empty text, chunker produces 0 chunks, 0 Qdrant points.
upsert_document() is called with ingest_status='indexed' and section_count=0.
Tenant queries the document → zero results, no error message, no indication the document failed to parse.
Proposed Fix
doc=fitz.open(stream=data, filetype='pdf')
ifdoc.needs_passordoc.is_encrypted:
doc.close()
raiseParseError(
f"Document is password-protected and cannot be parsed. "f"Please provide an unencrypted version."
)
Add this check immediately after fitz.open() and before iterating pages. Map ParseError to a 422 at the API layer so the caller receives actionable feedback.
Activity
piyushmakhija28 commentedon Jun 21, 2026
Fixed in PR #280 (Sprint R3/R4: fix(sprint-r3): root-cause fixes for all 39 bugs from code review round 3 + Sprint R4 round 4)