{"record":{"id":"eaf857be1946c073","repo":"odysseus-dev/odysseus","slug":"source-pdf-upload-id-not-found-in-uploads","errorCode":null,"errorMessage":"Source PDF {upload_id} not found in uploads","messagePattern":"Source PDF (.+?) not found in uploads","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"error","filePath":"routes/document/document_routes.py","lineNumber":1087,"sourceCode":"        any wrong values before triggering the actual download.\n        \"\"\"\n        from src.pdf_form_doc import find_source_upload_id, parse_markdown_to_values, load_field_sidecar\n\n        user = get_current_user(request)\n        db = SessionLocal()\n        try:\n            doc = db.query(Document).filter(Document.id == doc_id).first()\n            if not doc:\n                raise HTTPException(404, \"Document not found\")\n            _verify_doc_owner(db, doc, user)\n\n            upload_id = find_source_upload_id(doc.current_content or \"\")\n            if not upload_id:\n                raise HTTPException(400, \"Document is not linked to a source PDF\")\n\n            pdf_path = _locate_current_user_upload(request, upload_id, user)\n            if not pdf_path:\n                raise HTTPException(404, f\"Source PDF {upload_id} not found in uploads\")\n\n            fields = load_field_sidecar(pdf_path)\n            if not fields:\n                raise HTTPException(404, \"Field schema sidecar missing for source PDF\")\n\n            values = parse_markdown_to_values(doc.current_content or \"\")\n            field_meta = {f[\"name\"]: f for f in fields}\n\n            preview = []\n            for name, current in values.items():\n                meta = field_meta.get(name)\n                if not meta:\n                    continue\n                preview.append({\n                    \"name\": name,\n                    \"label\": meta.get(\"label\") or name,\n                    \"type\": meta.get(\"type\"),\n                    \"options\": meta.get(\"options\") or [],","sourceCodeStart":1069,"sourceCodeEnd":1105,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/document/document_routes.py#L1069-L1105","documentation":"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.","triggerScenarios":"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.","commonSituations":"Upload retention policy deletes files after N days; server migration moved the uploads folder; multi-user deployments where document ownership and upload ownership diverge.","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."],"exampleFix":"# before (server)\npdf_path = _locate_current_user_upload(request, upload_id, user)\nif not pdf_path:\n    raise HTTPException(404, f\"Source PDF {upload_id} not found in uploads\")\n\n# after (server)\npdf_path = _locate_current_user_upload(request, upload_id, user)\nif not pdf_path:\n    logger.warning(\"missing source upload %s for doc %s\", upload_id, doc.id)\n    raise HTTPException(404, f\"Source PDF {upload_id} not found in uploads\",\n                        headers={\"X-Reason\": \"upload-missing\"})  # client can offer re-upload","handlingStrategy":"fallback","validationCode":"import os\n\ndef source_upload_present(uploads_dir: str, upload_id: str, owner: str) -> bool:\n    return os.path.exists(os.path.join(uploads_dir, owner, upload_id)) or \\\n           os.path.exists(os.path.join(uploads_dir, upload_id))","typeGuard":null,"tryCatchPattern":"try:\n    p = requests.post(f\"{base}/api/document/{doc}/export-pdf/preview\")\nexcept requests.HTTPError as e:\n    if e.response.status_code == 404 and \"not found in uploads\" in e.response.json().get(\"detail\", \"\"):\n        prompt_reupload(doc)  # file gone; re-upload restores the link\n    else:\n        raise","preventionTips":["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."],"tags":["fastapi","http-404","file-management","pdf-export","uploads"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}