bytedance/deer-flow · warning · HTTPException
Too many files: maximum is {limits.max_files}
Error message
Too many files: maximum is {limits.max_files} What it means
Raised by the multi-file upload endpoint when the number of files in a single multipart request exceeds the configured limit (limits.max_files from the gateway's upload limits config). The endpoint counts form file parts before writing anything and rejects the whole request with HTTP 413 so no partial uploads occur. It exists to bound request handling cost per call.
Source
Thrown at backend/app/gateway/routers/uploads.py:314
except Exception:
return False
@router.post("", response_model=UploadResponse)
@require_permission("threads", "write", owner_check=True, require_existing=False)
async def upload_files(
thread_id: ThreadId,
request: Request,
files: list[UploadFile] = File(...),
config: AppConfig = Depends(get_config),
) -> UploadResponse:
"""Upload multiple files to a thread's uploads directory."""
if not files:
raise HTTPException(status_code=400, detail="No files provided")
limits = _get_upload_limits(config)
if len(files) > limits.max_files:
raise HTTPException(status_code=413, detail=f"Too many files: maximum is {limits.max_files}")
try:
effective_user_id = get_effective_user_id()
uploads_dir = await run_file_io(ensure_uploads_dir, thread_id, user_id=effective_user_id)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
sandbox_uploads = uploads_dir
uploaded_files = []
written_paths = []
sandbox_sync_targets = []
skipped_files = []
total_size = 0
# Track filenames within this request so duplicate form parts do not
# silently truncate each other. Existing uploads keep the historical
# overwrite behavior for a single replacement upload.
seen_filenames: set[str] = set()
sandbox_provider = get_sandbox_provider()View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Split the upload into batches of at most limits.max_files files per request.
- Check GET /threads/{thread_id}/uploads/limits (the endpoint that returns _get_upload_limits) before uploading and chunk accordingly.
- If the use case legitimately needs more files per call, raise the max_files upload limit in the gateway config and restart.
Example fix
// before
const form = new FormData();
allFiles.forEach(f => form.append('files', f));
await fetch(`/api/threads/${tid}/uploads`, {method:'POST', body: form});
// after
const limits = await fetch(`/api/threads/${tid}/uploads/limits`).then(r=>r.json());
for (const batch of chunk(allFiles, limits.max_files)) {
const form = new FormData();
batch.forEach(f => form.append('files', f));
await fetch(`/api/threads/${tid}/uploads`, {method:'POST', body: form});
} Defensive patterns
Strategy: validation
Validate before calling
const limits = await fetch(`/api/threads/${tid}/uploads/limits`).then(r => r.json());
const batches = chunk(files, limits.max_files);
// upload batches sequentially Prevention
- Fetch upload limits before building the multipart request and chunk client-side.
- Set the batching size from the limits endpoint, never hardcode it.
- Raise max_files in gateway config only when the workflow genuinely requires larger batches.
When it happens
Trigger: POST multipart/form-data to /threads/{thread_id}/uploads with more `files` parts than config.max_files (default set by the gateway's upload limits). E.g. attaching 11 files when max_files is 10.
Common situations: Bulk-attaching many documents in a chat UI; lowering max_files in config.yaml after clients were already batching large uploads; automated scripts that glob a directory and upload everything at once.
Related errors
- Failed to prepare ${failedConversions} attachment(s) for upl
- str(e)
- Failed to load agents: ${res.statusText}
- Agent '${name}' not found
- backend_unreachable
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/9353950e545dd14a.
Report an issue: GitHub.