bytedance/deer-flow · warning · HTTPException

Thread not found

Error message

Thread not found

What it means

Raised as HTTP 404 by POST /api/scheduled-tasks in reuse_thread mode when thread_store.check_access(thread_id, user_id, require_existing=True) fails — the thread does not exist, or exists but is not accessible/owned by the requesting user. Deliberately 404 (not 403) to avoid leaking thread existence.

Solutions

  1. Verify the thread exists and is owned by (or shared with) the current user via the threads endpoint.
  2. Use a fresh, valid thread id, or switch context_mode to fresh_thread_per_run.
  3. Re-check ownership if the thread belongs to a different user — access is enforced here.
Defensive patterns

Strategy: validation

Validate before calling

threads = requests.get(f"{BASE}/api/threads", headers=auth).json()
owned = {t["id"] for t in threads.get("threads", [])}
assert body["thread_id"] in owned, "thread missing or not accessible for this user"

Type guard

const isAccessibleThread = (tid: string, ownedIds: Set<string>): boolean => ownedIds.has(tid);

Try / catch

resp = requests.post(f"{BASE}/api/scheduled-tasks", json=body, headers=auth)
if resp.status_code == 404 and body["context_mode"] == "reuse_thread":
    body["thread_id"] = await pick_existing_thread()  # recover
    resp = requests.post(f"{BASE}/api/scheduled-tasks", json=body, headers=auth)

Prevention

When it happens

Trigger: Creating a reuse_thread task with a deleted thread id, another user's thread id, or a malformed id.

Common situations: Thread was deleted before the task was created; switching accounts; stale thread id cached in the UI.

Related errors


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

Appendix: source

Thrown at backend/app/gateway/routers/scheduled_tasks.py:83

    return await repo.list_by_user(str(user.id))


@router.post("/scheduled-tasks")
@require_permission("threads", "write")
async def create_scheduled_task(request: Request, body: ScheduledTaskCreateRequest):
    config = get_config()
    repo = get_scheduled_task_repo(request)
    thread_store = get_thread_store(request)
    user = await get_optional_user_from_request(request)
    if user is None:
        raise HTTPException(status_code=401, detail="Authentication required")
    if body.context_mode not in {"fresh_thread_per_run", "reuse_thread"}:
        raise HTTPException(status_code=422, detail="Unsupported context_mode")
    if body.context_mode == "reuse_thread":
        if not body.thread_id:
            raise HTTPException(status_code=422, detail="reuse_thread requires thread_id")
        if not await thread_store.check_access(body.thread_id, str(user.id), require_existing=True):
            raise HTTPException(status_code=404, detail="Thread not found")
    if body.schedule_type not in {"once", "cron"}:
        raise HTTPException(status_code=422, detail="Unsupported schedule_type")

    schedule_spec = dict(body.schedule_spec)
    try:
        validate_timezone(body.timezone)
        if body.schedule_type == "cron":
            raw_cron = schedule_spec.get("cron")
            if not isinstance(raw_cron, str):
                raise HTTPException(status_code=422, detail="cron schedule requires schedule_spec.cron")
            schedule_spec["cron"] = normalize_cron_expression(raw_cron)
        next_run_at = compute_next_run_at(
            body.schedule_type,
            schedule_spec,
            body.timezone,
            now=datetime.now(UTC),
        )
    except ValueError as exc:

View on GitHub (pinned to 1dd6ba1acb)