Hmbown/CodeWhale · error

Automation run has no durable task binding

Error message

Automation run has no durable task binding

What it means

This error is thrown by `enqueue_run_task` in the automation manager when an automation run record lacks a durable task binding. Dispatching a run requires BOTH a dispatch handle (`run.dispatch`) and a persisted task id (`run.task_id`); if either is `None`, the caller's contract (bind first, then enqueue) was violated, so the run cannot be scheduled onto the task manager.

Solutions

  1. Ensure the run is bound before enqueueing: create the task via the task manager, persist the binding, and only then call the code path that enqueues the run
  2. Check for code paths that set `run.task_id = None` (task removal/cleanup) that later retry the run
  3. Inspect the persisted run record to confirm the binding survived the restart/reload
  4. Log `run.dispatch.is_some()` and `run.task_id` at the call site to identify which half of the binding is missing

Example fix

// before
match result {
    Ok(task) => { ... }
}
// after
if run.dispatch.is_none() || run.task_id.is_none() {
    // bind first: create + persist the task, then re-enter enqueue_run_task
    bind_run_task(run, tasks).await?;
}
let result = enqueue_run_task(run, tasks).await;
Defensive patterns

Strategy: validation

Validate before calling

fn is_dispatchable(run: &AutomationRunRecord) -> bool {
    run.dispatch.is_some() && run.task_id.is_some()
}
if !is_dispatchable(&run) { return Err(anyhow!("run not bound")); }

Type guard

fn bound(run: &AutomationRunRecord) -> Option<(&Dispatch, &TaskId)> {
    Some((run.dispatch.as_ref()?, run.task_id.as_ref()?))
}

Prevention

When it happens

Trigger: Calling `enqueue_run_task` with an `AutomationRunRecord` whose `dispatch` or `task_id` field is `None` — i.e. the run was never bound to a task, or the binding was cleared/lost before enqueueing.

Common situations: Restoring automation state from disk where the task binding failed to persist; a run created but the task registration step failed silently; clearing a task (e.g. after cancellation) and then attempting to re-dispatch the run.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/ceb08d2516f345a0. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/automation_manager.rs:2131

            .recover_task_admission(dispatch.request.clone(), task_id.to_owned())
            .await?
    };
    crate::task_manager::validate_bound_task_request(&task, &dispatch.request)?;
    dispatch.accepted = true;
    dispatch.suppress_report = dispatch.delivery_mode == AutomationDeliveryMode::Watcher
        && task.status == TaskStatus::Completed
        && task
            .result_summary
            .as_deref()
            .is_some_and(|summary| summary.trim() == AUTOMATION_WATCHER_NO_REPORT_SENTINEL);
    Ok(task)
}

/// Caller owns dispatch.lock and has already persisted this exact binding.
async fn enqueue_run_task(run: &mut AutomationRunRecord, tasks: &SharedTaskManager) {
    let result = match (&mut run.dispatch, &run.task_id) {
        (Some(dispatch), Some(task_id)) => dispatch_bound_task(dispatch, task_id, tasks).await,
        _ => Err(anyhow::anyhow!(
            "Automation run has no durable task binding"
        )),
    };
    match result {
        Ok(task) => {
            run.error = None;
            apply_task_status(run, &task);
        }
        Err(error) => {
            // Keep the same pending identity after uncertain admission. A later
            // tick first looks for its canonical task; no new id is allocated.
            run.error = Some(format!(
                "Automation task admission needs recovery: {error:#}"
            ));
        }
    }
}

View on GitHub (pinned to 433685b202)