Skip to content

fix(parser): password-protected PDFs silently ingest as empty, causing undetectable data loss #242

Closed

Description

@piyushmakhija28

Summary

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 checked
for page in doc:
    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

  1. Tenant uploads a password-protected PDF.
  2. fitz.open() succeeds — encrypted PDFs are valid file structures.
  3. _parse_page() calls page.get_text('dict') on each page → returns {'blocks': []}.
  4. All pages produce empty text, chunker produces 0 chunks, 0 Qdrant points.
  5. upsert_document() is called with ingest_status='indexed' and section_count=0.
  6. 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')
if doc.needs_pass or doc.is_encrypted:
    doc.close()
    raise ParseError(
        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

  1. piyushmakhija28 commented on Jun 21, 2026

    @piyushmakhija28
    ContributorAuthor

    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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcode-reviewFound via automated code reviewpriority: criticalBlocks all requests or crashes startup

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions