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.

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.

Appendix: 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)