bytedance/deer-flow · error · HTTPException
str(e)
Error message
str(e)
What it means
HTTP 400 raised when ensure_uploads_dir fails with a ValueError while preparing the thread's uploads directory (e.g. the thread_id does not resolve to a valid directory path). The raw exception text is surfaced as the detail. It fires before any file is written, so the request is rejected atomically.
Source
Thrown at backend/app/gateway/routers/uploads.py:320
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()
sync_to_sandbox = not _uses_thread_data_mounts(sandbox_provider)
sandbox = None
if sync_to_sandbox:
sandbox_id = await sandbox_provider.acquire_async(thread_id, user_id=effective_user_id)
sandbox = sandbox_provider.get(sandbox_id)
if sandbox is None:View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Verify the thread exists via the threads API before uploading and use the exact id it returned.
- Inspect the returned detail message — it is the ValueError text from ensure_uploads_dir and names the concrete problem (invalid id, unresolvable path).
- Re-create the thread and retry the upload if the thread was deleted.
Defensive patterns
Strategy: validation
Validate before calling
const thread = await fetch(`/api/threads/${encodeURIComponent(tid)}`).then(r => r.ok ? r.json() : null);
if (!thread) throw new Error('Thread does not exist'); Try / catch
catch 400 on upload; surface detail text (it names the invalid id/path) and stop — do not blind-retry.
Prevention
- Always create threads via the API and reuse the returned id.
- Drop local thread state when the UI sees a 404/400 on the thread itself.
When it happens
Trigger: Uploading to a thread whose id is malformed or cannot be mapped to a storage path (ensure_uploads_dir raises ValueError); uploading to a thread that was deleted or whose per-user directory cannot be created/resolved.
Common situations: Client generates a thread id locally instead of using one returned by the threads API; a thread was deleted while an upload was in flight; user-context (effective_user_id) resolves to a path segment the storage layer rejects.
Related errors
- Too many files: maximum is {limits.max_files}
- Invalid path
- checkpoint thread_id does not match request thread_id
- Failed to prepare ${failedConversions} attachment(s) for upl
- Thread is not ready for file upload.
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/4851b4b6e6320ce7.
Report an issue: GitHub.