odysseus-dev/odysseus · error · HTTPException
Upload failed: {e}
Error message
Upload failed: {e} What it means
500 wrapper raised by POST /api/documents/import-pdf when upload_handler.save_upload(file, client_ip, owner=user) throws an unexpected exception (HTTPException from inside save_upload is passed through unchanged). save_upload is responsible for writing the multipart file to disk, so failures are almost always I/O or storage related; the original exception text follows 'Upload failed:'.
Source
Thrown at routes/document/document_routes.py:266
# doc (the doc creators below already handle a missing session).
if session_id:
db = SessionLocal()
try:
_get_session_or_404(db, session_id, user)
finally:
db.close()
if upload_handler is None:
raise HTTPException(500, "Upload handler not configured")
client_ip = request.client.host if request.client else "unknown"
try:
meta = upload_handler.save_upload(file, client_ip, owner=user)
except HTTPException:
raise
except Exception as e:
logger.error(f"PDF import save_upload failed: {e}")
raise HTTPException(500, f"Upload failed: {e}")
upload_id = meta["id"]
pdf_path = _locate_current_user_upload(request, upload_id, user)
if not pdf_path:
raise HTTPException(500, "Saved PDF could not be located")
title = os.path.splitext(meta.get("original_name") or meta.get("name") or upload_id)[0]
try:
body_text = strip_pdf_content_marker(_process_pdf(pdf_path, owner=user))
except Exception:
body_text = None
is_form = False
try:
is_form = has_form_fields(pdf_path)
except Exception as e:
logger.warning(f"has_form_fields failed for {pdf_path}: {e}")
View on GitHub (pinned to f9235ebbf1)
Solutions
- Check the server log line 'PDF import save_upload failed: <e>' for the underlying OSError/exception.
- Verify the upload directory exists and the service user has write permission (mkdir -p, chown, or fix the volume mount).
- Confirm free disk space and quotas on the upload volume.
- If the cause is an internal size/validation check thrown as a plain exception, convert it to HTTPException(413/400) inside save_upload so it propagates cleanly.
Example fix
# before # upload dir never created; save_upload raises PermissionError -> 500 Upload failed # after UploadHandler(base_dir, upload_dir) # ensure upload_dir is created at init: # os.makedirs(upload_dir, exist_ok=True) inside UploadHandler.__init__
Defensive patterns
Strategy: retry
Validate before calling
async function safeImport(fd) {
// pre-flight: file present and non-empty
if (!fd.get('file')?.size) throw new Error('empty file');
return api.post('/api/documents/import-pdf', fd, { timeout: 120000 });
} Try / catch
try { meta = await api.post('/api/documents/import-pdf', fd); }
catch (e) {
if (/Upload failed/.test(e.message)) {
if (isTransient(e)) await retryWithBackoff(() => api.post('/api/documents/import-pdf', fd), 3);
else notify('Upload failed: ' + e.message);
} else throw e;
} Prevention
- Monitor free disk space and upload-dir writability with a healthcheck
- Cap upload size client-side to avoid internal limit exceptions
- Give the API process ownership of its uploads directory
When it happens
Trigger: Upload directory does not exist or is not writable by the API process; disk full; the uploaded file exceeds a size cap enforced inside save_upload with a non-HTTP exception; filename encoding problems producing an OSError on write.
Common situations: Containerized deployment where the uploads volume is missing or owned by root; disk quota exhausted on a long-running dev box; SELinux/AppArmor denying writes; very large PDFs hitting an internal limit implemented as ValueError instead of HTTPException.
Related errors
- Saved PDF could not be located
- Failed to create document for PDF
- Source PDF could not be located
- Extraction failed: {e}
- Skill import failed
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/1c2a73efbf2c3cd9.
Report an issue: GitHub.