{"record":{"id":"c84040981735ae61","repo":"odysseus-dev/odysseus","slug":"saved-pdf-could-not-be-located","errorCode":null,"errorMessage":"Saved PDF could not be located","messagePattern":"Saved PDF could not be located","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"routes/document/document_routes.py","lineNumber":271,"sourceCode":"            finally:\n                db.close()\n\n        if upload_handler is None:\n            raise HTTPException(500, \"Upload handler not configured\")\n\n        client_ip = request.client.host if request.client else \"unknown\"\n        try:\n            meta = upload_handler.save_upload(file, client_ip, owner=user)\n        except HTTPException:\n            raise\n        except Exception as e:\n            logger.error(f\"PDF import save_upload failed: {e}\")\n            raise HTTPException(500, f\"Upload failed: {e}\")\n\n        upload_id = meta[\"id\"]\n        pdf_path = _locate_current_user_upload(request, upload_id, user)\n        if not pdf_path:\n            raise HTTPException(500, \"Saved PDF could not be located\")\n\n        title = os.path.splitext(meta.get(\"original_name\") or meta.get(\"name\") or upload_id)[0]\n        try:\n            body_text = strip_pdf_content_marker(_process_pdf(pdf_path, owner=user))\n        except Exception:\n            body_text = None\n\n        is_form = False\n        try:\n            is_form = has_form_fields(pdf_path)\n        except Exception as e:\n            logger.warning(f\"has_form_fields failed for {pdf_path}: {e}\")\n\n        if is_form:\n            fields = extract_fields(pdf_path)\n            save_field_sidecar(pdf_path, fields)\n            doc_id = create_form_markdown_document(\n                session_id=session_id,","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/document/document_routes.py#L253-L289","documentation":"500 raised by POST /api/documents/import-pdf when save_upload reported success and returned an id, but _locate_current_user_upload(request, upload_id, user) could not resolve that id to a readable file path for the requesting user. It means the handler stored the file (or its metadata) somewhere the path-resolution step does not look, or ownership scoping filtered it out.","triggerScenarios":"save_upload writes to a directory different from the handler's configured upload_dir used during resolution; the upload record stores an owner other than the requesting user (auth aliasing, e.g. user vs user:ip key mismatch); async write not yet flushed to disk when resolution runs; file id in metadata ('id') differs from the stored filename.","commonSituations":"Two UploadHandler instances configured with different base dirs (one injected into routes, another used by the upload service); username normalization (case, domain stripping) applied on one side only; tests that mock save_upload to return a fake id.","solutions":["Confirm the same UploadHandler instance/object is used for both save_upload and path resolution (the one captured by setup_document_routes).","Compare the owner value stored with the upload against get_current_user(request) — they must match exactly, including case.","Inspect the upload directory: does a file named <upload_id> (or mapped to it) actually exist right after the failing request?","Log resolved upload_dir and the candidate paths inside _resolve_user_upload_path to see which side of the mismatch it is."],"exampleFix":"# before\nhandler_a = UploadHandler('/tmp/uploads')   # used by upload service\nhandler_b = UploadHandler('/var/uploads')   # injected into routes -> 500\n# after\nshared = UploadHandler('/var/uploads')\napp.include_router(setup_document_routes(session_manager, shared))","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await api.post('/api/documents/import-pdf', fd); }\ncatch (e) {\n  if (/could not be located/.test(e.message)) {\n    // storage inconsistency: offer re-upload instead of retry\n    offerReupload();\n  } else throw e;\n}","preventionTips":["Use one shared UploadHandler instance for saving and resolution","Keep the uploads volume on durable storage","Log upload_id plus resolved path server-side to catch config drift early"],"tags":["upload","filesystem","path-resolution","ownership","http-500"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}