bytedance/deer-flow · warning · HTTPException
Thread has a run in flight. Set the goal after the run finis
Error message
Thread has a run in flight. Set the goal after the run finishes.
What it means
409 from PUT /threads/{thread_id}/goal when reserve_checkpoint_write raises ConflictError — the thread currently has an in-flight run holding the checkpoint write reservation, and goal writes are excluded to avoid clobbering live state. The error is deliberate serialization, telling the caller to wait.
Source
Thrown at backend/app/gateway/routers/threads.py:1105
@router.put("/{thread_id}/goal", response_model=ThreadGoalResponse)
@require_permission("threads", "write", owner_check=True)
async def set_thread_goal(thread_id: ThreadId, body: ThreadGoalRequest, request: Request) -> ThreadGoalResponse:
"""Set or replace the active goal for a thread.
``/chats/new`` pages already hold a generated UUID before the first run, so
this endpoint creates the missing thread checkpoint on demand.
"""
checkpointer = get_checkpointer(request)
try:
goal = build_goal_state(body.objective, max_continuations=body.max_continuations)
async with reserve_checkpoint_write(request, thread_id, user_id=get_effective_user_id()):
await _ensure_thread_for_goal(thread_id, request)
await write_thread_goal(checkpointer, thread_id, goal, as_node="goal", create_if_missing=True)
except ValueError as exc:
raise HTTPException(status_code=422, detail=str(exc)) from exc
except ConflictError:
raise HTTPException(status_code=409, detail="Thread has a run in flight. Set the goal after the run finishes.") from None
except HTTPException:
raise
except Exception:
logger.exception("Failed to set goal for thread %s", sanitize_log_param(thread_id))
raise HTTPException(status_code=500, detail="Failed to set thread goal") from None
return ThreadGoalResponse(goal=goal)
@router.delete("/{thread_id}/goal", response_model=ThreadGoalResponse)
@require_permission("threads", "write", owner_check=True)
async def clear_thread_goal(thread_id: ThreadId, request: Request) -> ThreadGoalResponse:
"""Clear the active goal for a thread."""
checkpointer = get_checkpointer(request)
try:
async with reserve_checkpoint_write(request, thread_id, user_id=get_effective_user_id()):
await write_thread_goal(checkpointer, thread_id, None, as_node="goal")
except ConflictError:
raise HTTPException(status_code=409, detail="Thread has a run in flight. Clear the goal after the run finishes.") from NoneView on GitHub (pinned to 1dd6ba1acb)
Solutions
- Wait for the active run to finish (poll GET /threads/{id} status or the stream completion) and retry the PUT.
- In the UI, disable goal editing while the thread status is not idle.
- If no run is actually active but the 409 persists, the reservation leaked — restart the Gateway or clear the reservation state per its docs, then retry.
- Sequence goal updates between runs in automation rather than racing them.
Example fix
// before
await api.put(`/threads/${id}/goal`, {objective}); // 409 during a run
// after
let s = await api.get(`/threads/${id}`).status;
while (s !== 'idle') { await waitForRunCompletion(id); s = (await api.get(`/threads/${id}`)).status; }
await api.put(`/threads/${id}/goal`, {objective}); Defensive patterns
Strategy: retry
Validate before calling
const {status} = await api.get(`/api/threads/${id}`);
if (status !== 'idle') { /* queue the goal update; PUT after the run completes */ } Type guard
async function threadIdle(id: string): Promise<boolean> {
const t = await api.get(`/api/threads/${id}`);
return t.status === 'idle';
} Try / catch
try { await api.put(`/api/threads/${id}/goal`, body); }
catch (err) {
if (err.status === 409 && /run in flight/.test(err.detail)) { await waitForRunCompletion(id); retryOnce(); }
else throw err;
} Prevention
- Disable goal editing in the UI while the thread is streaming/running.
- Sequence goal updates between runs in automation instead of racing them.
- A persistent 409 with no visible run indicates a leaked reservation — restart the Gateway rather than retrying harder.
When it happens
Trigger: PUT /goal while a run/stream on the same thread is executing (agent streaming a response, background goal continuation active), or a stale reservation left by a run that died without releasing.
Common situations: User edits the goal in the UI while the agent is mid-run; automated loops setting goals concurrently with runs; a crashed worker whose reservation was not reclaimed yet.
Related errors
- Thread has a run in flight. Save after the run finishes.
- Branching is only available in the main conversation.
- Agent '{normalized_name}' already exists
- Scheduled task is currently running; retry after the active
- Failed to create thread
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/74ace1ad67cdd341.
Report an issue: GitHub.