odysseus-dev/odysseus · warning · HTTPException
No files uploaded
Error message
No files uploaded
What it means
HTTP 400 from POST /api/uploads when the multipart form contains no file parts — FastAPI's File(...) dependency yields an empty list and the handler rejects it before any rate limiting or storage work.
Source
Thrown at routes/upload_routes.py:267
return image_id
except Exception as e:
db.rollback()
logger.warning("Failed to add chat image upload to gallery: %s", e)
return None
finally:
db.close()
@router.post("")
async def api_upload(
request: Request,
files: List[UploadFile] = File(...),
session_id: Optional[str] = Form(None),
):
"""Upload files with enhanced security and organization."""
if not isinstance(session_id, str):
session_id = None
if not files:
raise HTTPException(400, "No files uploaded")
client_ip = request.client.host if request.client else "unknown"
out = []
# Limit concurrent uploads per IP. Count genuine recent upload events —
# NOT the number of files in this batch. The previous check summed over
# `files`, so a single multi-file request counted itself as N concurrent
# uploads and tripped the limit (issue #1346: "attach more than one file
# → the model doesn't even see them"). save_upload still enforces the
# per-minute sliding-window rate limit per file.
recent_uploads = count_recent_uploads(
upload_handler.upload_rate_log.get(client_ip, []), time.time()
)
if recent_uploads >= upload_handler.max_concurrent_uploads:
raise HTTPException(
status_code=429,
detail=f"Maximum concurrent uploads ({upload_handler.max_concurrent_uploads}) exceeded"View on GitHub (pinned to f9235ebbf1)
Solutions
- Append each file as a FormData entry named exactly 'files'
- Client-side guard: block submit when no file is selected
- Send Content-Type: multipart/form-data — let the browser/curl set it; do not force application/json
Example fix
# before curl -X POST https://host/api/uploads -F session_id=abc # 400 No files uploaded # after curl -X POST https://host/api/uploads -F session_id=abc -F 'files=@doc.pdf'
Defensive patterns
Strategy: validation
Validate before calling
const files = fileInput.files;
if (!files || files.length === 0) { showUserWarning('Select at least one file'); return; }
const fd = new FormData();
[...files].forEach(f => fd.append('files', f)); Type guard
function hasFiles(files: FileList | null): files is FileList & { length: number } {
return !!files && files.length > 0;
} Try / catch
if (resp.status === 400 && body.detail === 'No files uploaded') { fixFormDataFieldNames(); } Prevention
- The multipart field name must be exactly 'files'
- Always client-side validate non-empty file inputs before submit
- Send multipart/form-data, not JSON
When it happens
Trigger: Sending the multipart body with zero file fields; using a form field name other than 'files' (the endpoint binds the literal name 'files'); sending JSON instead of multipart/form-data so FastAPI binds no files.
Common situations: Frontend FormData appending files under a different key or forgetting to append before fetch; empty file-input submission without client-side validation; curl command missing -F 'files=@...' parts.
Related errors
- Invalid remote_host — must be host or user@host, no SSH opti
- Invalid ssh_port
- Unknown wipe kind: {kind!r}
- HTTP ${res.status}${body ? ': ' + body.slice(0, 160) : ''}
- Failed to save signature
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/7ab9b9a5725e7f2a.
Report an issue: GitHub.