bytedance/deer-flow · warning · HTTPException

File not found: {filename}

Error message

File not found: {filename}

What it means

HTTP 404 raised by DELETE /threads/{thread_id}/uploads/{filename} when the storage layer raises FileNotFoundError — the named file does not exist in that thread's uploads directory. Ownership/permission checks (owner_check=True, require_existing=True) have already passed, so this is purely a missing-file signal.

Source

Thrown at backend/app/gateway/routers/uploads.py:476

@require_permission("threads", "read", owner_check=True)
async def list_uploaded_files(thread_id: ThreadId, request: Request) -> UploadListResponse:
    """List all files in a thread's uploads directory."""
    try:
        result = await run_file_io(_list_uploaded_files_for_thread, thread_id, get_effective_user_id())
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))

    return UploadListResponse(**result)


@router.delete("/{filename}")
@require_permission("threads", "delete", owner_check=True, require_existing=True)
async def delete_uploaded_file(thread_id: ThreadId, filename: str, request: Request) -> dict:
    """Delete a file from a thread's uploads directory."""
    try:
        return await run_file_io(_delete_uploaded_file_for_thread, thread_id, filename, get_effective_user_id())
    except FileNotFoundError:
        raise HTTPException(status_code=404, detail=f"File not found: {filename}")
    except PathTraversalError:
        raise HTTPException(status_code=400, detail="Invalid path")
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error(f"Failed to delete {filename}: {e}")
        raise HTTPException(status_code=500, detail=f"Failed to delete {filename}: {str(e)}")

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Refresh the file list (GET .../uploads/list) and delete using the exact filename it returns.
  2. Treat 404 on delete as success if the goal is 'file gone' (idempotent delete in the client).

Example fix

// before
await api.deleteUpload(tid, name); // throws on 404

// after
try { await api.deleteUpload(tid, name); }
catch (e) { if (e.status !== 404) throw e; /* already gone */ }
Defensive patterns

Strategy: try-catch

Validate before calling

const files = await fetch(`/api/threads/${tid}/uploads/list`).then(r => r.json());
const target = files.files.find(f => f.filename === name);
if (!target) return; // nothing to delete

Try / catch

catch (e) { if (e.status === 404) return; /* treat as deleted */ throw e; }

Prevention

When it happens

Trigger: DELETE on an uploads filename that was never uploaded, was already deleted, or whose on-disk name differs from the URL segment (e.g. it was renamed by duplicate-handling suffixing or normalized).

Common situations: UI list is stale after a concurrent delete; filename was normalized/suffixed at upload time so the client's original name no longer matches; retrying a delete that already succeeded.

Related errors


AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14). Data as JSON: /api/errors/fcb629a3b5efcf43. Report an issue: GitHub.