bytedance/deer-flow · warning · HTTPException
Scheduled task is currently running; retry after the active
Error message
Scheduled task is currently running; retry after the active execution finishes
What it means
Raised as HTTP 409 by _ensure_task_mutable when attempting to mutate (PATCH/DELETE, or any operation guarded by this helper) a scheduled task whose status is 'running'. The system prevents editing tasks mid-execution to keep the active run's inputs stable; retry after the execution completes.
Source
Thrown at backend/app/gateway/routers/scheduled_tasks.py:33
get_scheduled_task_run_repo,
get_scheduled_task_service,
get_thread_store,
)
from deerflow.scheduler.schedules import (
next_run_at as compute_next_run_at,
)
from deerflow.scheduler.schedules import (
normalize_cron_expression,
validate_timezone,
)
from deerflow.utils.thread_id import ThreadId
router = APIRouter(prefix="/api", tags=["scheduled-tasks"])
def _ensure_task_mutable(task: dict[str, Any]) -> None:
if task.get("status") == "running":
raise HTTPException(
status_code=409,
detail="Scheduled task is currently running; retry after the active execution finishes",
)
class ScheduledTaskCreateRequest(BaseModel):
thread_id: ThreadId | None = None
context_mode: str = "fresh_thread_per_run"
title: str = Field(min_length=1)
prompt: str = Field(min_length=1)
schedule_type: str
schedule_spec: dict[str, Any]
timezone: str
class ScheduledTaskUpdateRequest(BaseModel):
context_mode: str | None = None
thread_id: ThreadId | None = NoneView on GitHub (pinned to 1dd6ba1acb)
Solutions
- Wait for the active execution to finish, verify status != 'running' via GET /api/scheduled-tasks/{task_id}, then retry the mutation.
- For automation, implement retry-with-backoff on 409 with a cap rather than immediate retry.
- If the task appears stuck in 'running' (crashed scheduler), restart the scheduler service so it finalizes the run state, then retry.
Example fix
# before
resp = requests.patch(f"{BASE}/api/scheduled-tasks/{task_id}", json=patch)
# after: retry on 409
for attempt in range(5):
resp = requests.patch(f"{BASE}/api/scheduled-tasks/{task_id}", json=patch)
if resp.status_code != 409:
break
time.sleep(10 * (attempt + 1)) Defensive patterns
Strategy: retry
Validate before calling
task = requests.get(f"{BASE}/api/scheduled-tasks/{task_id}", headers=auth).json()
if task.get("status") == "running":
defer_mutation(task_id) # do not send PATCH/DELETE yet Type guard
def is_task_mutable(task: dict) -> bool:
return task.get("status") != "running" Try / catch
for attempt in range(MAX_RETRIES):
resp = requests.patch(url, json=patch, headers=auth)
if resp.status_code != 409:
break
time.sleep(backoff(attempt)) # 409 = running; wait for execution to finish
resp.raise_for_status() Prevention
- Check task status via GET before showing edit/delete affordances in the UI.
- Back off exponentially on 409 — execution length is unknown.
- Keep mutations idempotent so a delayed retry after execution is safe.
When it happens
Trigger: PATCH or DELETE /api/scheduled-tasks/{task_id} while the scheduler is actively executing that task; also any code path calling _ensure_task_mutable on a task dict with status == 'running'.
Common situations: User edits a cron task exactly as it fires; long-running scheduled execution overlapping a UI save; automation retrying an update immediately after start.
Related errors
- Thread has a run in flight. Save after the run finishes.
- Thread has a run in flight. Set the goal after the run finis
- HTTP ${response.status}: ${response.statusText}
- An account with this email already exists. Contact your admi
- Scheduled task repo not available
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/64837502de10e5ec.
Report an issue: GitHub.