odysseus-dev/odysseus · error · HTTPException
Source PDF {upload_id} not found in uploads
Error message
Source PDF {upload_id} not found in uploads What it means
Raised by POST /api/document/{doc_id}/export-pdf/preview with 404 when the document references a source upload_id but _locate_current_user_upload(request, upload_id, user) cannot find that file among the current user's uploads. The document-to-PDF link exists; the underlying file does not (anymore) — deleted upload, wrong owner, or uploads directory layout changed.
Source
Thrown at routes/document/document_routes.py:1087
any wrong values before triggering the actual download.
"""
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 in uploads")
fields = load_field_sidecar(pdf_path)
if not fields:
raise HTTPException(404, "Field schema sidecar missing for source PDF")
values = parse_markdown_to_values(doc.current_content or "")
field_meta = {f["name"]: f for f in fields}
preview = []
for name, current in values.items():
meta = field_meta.get(name)
if not meta:
continue
preview.append({
"name": name,
"label": meta.get("label") or name,
"type": meta.get("type"),
"options": meta.get("options") or [],View on GitHub (pinned to f9235ebbf1)
Solutions
- Check the uploads directory for the file named by the upload_id in the error message.
- If the file was deleted, re-upload the same PDF so the id resolves again, or drop the export-PDF flow for this document.
- Align upload ownership with document ownership so the owner-scoped lookup can see the file.
- Exempt PDF-form source uploads from retention-based cleanup.
Example fix
# before (server)
pdf_path = _locate_current_user_upload(request, upload_id, user)
if not pdf_path:
raise HTTPException(404, f"Source PDF {upload_id} not found in uploads")
# after (server)
pdf_path = _locate_current_user_upload(request, upload_id, user)
if not pdf_path:
logger.warning("missing source upload %s for doc %s", upload_id, doc.id)
raise HTTPException(404, f"Source PDF {upload_id} not found in uploads",
headers={"X-Reason": "upload-missing"}) # client can offer re-upload Defensive patterns
Strategy: fallback
Validate before calling
import os
def source_upload_present(uploads_dir: str, upload_id: str, owner: str) -> bool:
return os.path.exists(os.path.join(uploads_dir, owner, upload_id)) or \
os.path.exists(os.path.join(uploads_dir, upload_id)) Try / catch
try:
p = requests.post(f"{base}/api/document/{doc}/export-pdf/preview")
except requests.HTTPError as e:
if e.response.status_code == 404 and "not found in uploads" in e.response.json().get("detail", ""):
prompt_reupload(doc) # file gone; re-upload restores the link
else:
raise Prevention
- Exclude source-upload PDFs from retention/cleanup jobs.
- Persist and migrate the uploads volume together with the DB.
- Keep upload ownership aligned with document ownership.
When it happens
Trigger: The uploaded PDF was removed via an uploads-cleanup job while the derived document survived; the document belongs to another user and the ownership-scoped upload lookup excludes it; uploads stored under a different directory root than the resolver expects.
Common situations: Upload retention policy deletes files after N days; server migration moved the uploads folder; multi-user deployments where document ownership and upload ownership diverge.
Related errors
- Field schema sidecar missing for source PDF
- Source PDF {upload_id} not found
- 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/eaf857be1946c073.
Report an issue: GitHub.