odysseus-dev/odysseus · error · HTTPException
Source PDF {upload_id} not found
Error message
Source PDF {upload_id} not found What it means
Raised by GET /api/document/{doc_id}/pdf-form-layout with 404 when the document references a source upload_id but _locate_current_user_upload cannot find the corresponding PDF file for the current user. The layout endpoint then has nothing for fitz.open() to render, so it fails before any page-image work.
Source
Thrown at routes/document/document_routes.py:1149
/page/{n}.png returns at the same DPI) plus the list of form fields
on that page with their rects translated to image-pixel coordinates.
Frontend overlays HTML form controls at those positions.
"""
from src.pdf_form_doc import find_source_upload_id, parse_markdown_to_values, load_field_sidecar
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)
upload_id = find_source_upload_id(doc.current_content or "")
if not upload_id:
raise HTTPException(400, "Document is not linked to a source PDF")
pdf_path = _locate_current_user_upload(request, upload_id, user)
if not pdf_path:
raise HTTPException(404, f"Source PDF {upload_id} not found")
fitz = _load_pdf_viewer_fitz()
schema = load_field_sidecar(pdf_path) or []
values = parse_markdown_to_values(doc.current_content or "")
# Group fields by page
by_page: Dict[int, list] = {}
for f in schema:
by_page.setdefault(f["page"], []).append(f)
scale = _PDF_RENDER_SCALE
pdf_doc = fitz.open(pdf_path)
try:
pages_out = []
for page_index in range(pdf_doc.page_count):
page = pdf_doc[page_index]
page_no = page_index + 1
pw, ph = page.rect.width, page.rect.heightView on GitHub (pinned to f9235ebbf1)
Solutions
- Confirm the file for the upload_id in the error exists under the expected uploads path and owner.
- Restore the missing PDF (re-upload) or fall back to the plain markdown editor for this document.
- Persist the uploads volume across restarts and migrate it together with the database.
- Exclude source-upload PDFs from automated file cleanup.
Example fix
# before
pdf_path = _locate_current_user_upload(request, upload_id, user)
if not pdf_path:
raise HTTPException(404, f"Source PDF {upload_id} not found")
# after
pdf_path = _locate_current_user_upload(request, upload_id, user)
if not pdf_path:
raise HTTPException(
404, f"Source PDF {upload_id} not found",
headers={"X-Reason": "upload-missing"}) # client offers re-upload / plain editor Defensive patterns
Strategy: fallback
Validate before calling
import os
def source_pdf_reachable(uploads_dir: str, upload_id: str, owner: str) -> bool:
return any(os.path.exists(p) for p in (
os.path.join(uploads_dir, owner, upload_id),
os.path.join(uploads_dir, upload_id),
)) Try / catch
try:
layout = requests.get(f"{base}/api/document/{doc}/pdf-form-layout")
except requests.HTTPError as e:
if e.response.status_code == 404 and "Source PDF" in e.response.json().get("detail", ""):
open_plain_editor(doc) # file missing — degrade gracefully
else:
raise Prevention
- Persist the uploads volume; migrate it with the database.
- Never let cleanup remove source PDFs whose derived documents remain active.
- Offer the plain editor as fallback when the source file is unrecoverable.
When it happens
Trigger: Source PDF deleted from uploads by a cleanup job while the derived document remains; ownership mismatch — the document is visible to the user but the upload belongs to a different owner; uploads directory moved or remounted after deploy.
Common situations: Upload retention pruning; containerized deployments with non-persistent upload volumes; shared/migrated documents whose files did not follow.
Related errors
- Source PDF {upload_id} not found in uploads
- Field schema sidecar missing for source PDF
- Integration not found
- Calendar not found
- Event not found
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/7d438d0a7f08cdfe.
Report an issue: GitHub.