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 = None

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Wait for the active execution to finish, verify status != 'running' via GET /api/scheduled-tasks/{task_id}, then retry the mutation.
  2. For automation, implement retry-with-backoff on 409 with a cap rather than immediate retry.
  3. 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

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


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