odysseus-dev/odysseus · warning · HTTPException

Source PDF could not be located

Error message

Source PDF could not be located

What it means

404 from POST /api/document/{doc_id}/extract-pdf-text: the doc has a valid pdf_source marker with an upload_id, but _locate_current_user_upload cannot resolve that upload to an existing file for the current user. The underlying PDF file backing the document is gone or inaccessible.

Source

Thrown at routes/document/document_routes.py:527

        from src.document_processor import _process_pdf, strip_pdf_content_marker
        from src.pdf_form_doc import find_source_upload_id

        user = get_current_user(request)
        db = SessionLocal()
        try:
            doc = db.query(Document).filter(Document.id == doc_id).first()
            if not doc:
                raise HTTPException(404, "Document not found")
            _verify_doc_owner(db, doc, user)

            content = doc.current_content or ""
            upload_id = find_source_upload_id(content)
            if not upload_id:
                raise HTTPException(400, "Document is not a PDF — no pdf_source marker found")

            pdf_path = _locate_current_user_upload(request, upload_id, user)
            if not pdf_path:
                raise HTTPException(404, "Source PDF could not be located")

            try:
                body_text = strip_pdf_content_marker(_process_pdf(pdf_path, owner=user))
            except Exception as e:
                logger.error(f"extract_pdf_text failed for {pdf_path}: {e}")
                raise HTTPException(500, f"Extraction failed: {e}")

            if not body_text:
                return {"ok": True, "id": doc_id, "extracted": False, "reason": "No readable content"}

            # Preserve everything up through the title (front-matter marker +
            # first H1) and replace the rest with the freshly extracted text.
            head_re = re.compile(r'^(<!--[^>]+-->\s*\n+#[^\n]*\n+)', re.MULTILINE)
            head_match = head_re.match(content)
            head = head_match.group(1) if head_match else (content.splitlines()[0] + "\n\n# " + (doc.title or "PDF") + "\n\n")
            doc.current_content = head + body_text.strip() + "\n"
            doc.version_count = (doc.version_count or 1) + 1
            db.add(DocumentVersion(

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Check whether the file for the marker's upload_id still exists in the uploads directory.
  2. If uploads were lost, re-import the PDF to create a new upload + doc linkage.
  3. Persist uploads on a durable volume and exclude it from cleanup jobs while docs still reference it.
  4. Verify the requesting user matches the upload's owner for shared/legacy docs.

Example fix

# before
# container restarted with fresh empty /uploads volume; extract returns 404
# after
# mount the persistent uploads volume, e.g.
# docker run -v document_uploads:/var/app/uploads ...
Defensive patterns

Strategy: fallback

Try / catch

try { return await api.post(`/api/document/${id}/extract-pdf-text`); }
catch (e) {
  if (e.status === 404 && /Source PDF/.test(e.message)) {
    return offerReimport(id); // file is gone; recreate linkage by re-uploading
  }
  throw e;
}

Prevention

When it happens

Trigger: Upload files cleaned up by a retention/GC job while the doc row remains; uploads volume not mounted after a container restart/migration; the upload's owner differs from the requesting user so ownership scoping filters the path; file moved by a storage refactor.

Common situations: Restoring a database backup without the uploads directory; ephemeral container storage losing /uploads; cross-user access attempts on a doc whose upload belongs to someone else.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/06ecbc2c17f896c3. Report an issue: GitHub.