Hmbown/CodeWhale · error · anyhow::Error

Task not found: {task_id}

Error message

Task not found: {task_id}

What it means

TaskManager::get_task_for_active_runtime looks up a task by its full durable id, stamped onto a trusted runtime thread rather than model input. The lookup fails closed in two cases: the id is absent from the task map, or the record has owner_session_id == None (a legacy ownerless task). So even a restored, active task without an owner is rejected.

Source

Thrown at crates/tui/src/task_manager.rs:1423

        id_or_prefix: &str,
        owner_session_id: &str,
    ) -> Result<TaskRecord> {
        self.get_task_visible_to(id_or_prefix, Some(owner_session_id))
            .await
    }

    /// Retrieve the exact owned task stamped onto a trusted runtime thread.
    ///
    /// The runtime thread supplies a full durable id rather than model input.
    /// Legacy ownerless tasks fail closed even when restored as active.
    pub(crate) async fn get_task_for_active_runtime(&self, task_id: &str) -> Result<TaskRecord> {
        let state = self.state.lock().await;
        state
            .tasks
            .get(task_id)
            .filter(|task| task.owner_session_id.is_some())
            .cloned()
            .ok_or_else(|| anyhow!("Task not found: {task_id}"))
    }

    async fn get_task_visible_to(
        &self,
        id_or_prefix: &str,
        owner_session_id: Option<&str>,
    ) -> Result<TaskRecord> {
        let state = self.state.lock().await;
        let id = resolve_task_id_visible_to(&state.tasks, id_or_prefix, owner_session_id)?;
        state
            .tasks
            .get(&id)
            .cloned()
            .ok_or_else(|| anyhow!("Task not found: {id_or_prefix}"))
    }

    /// Cancel a queued or running task by id/prefix.
    pub async fn cancel_task(&self, id_or_prefix: &str) -> Result<TaskCancellation> {

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Use the exact task id returned by the enqueue/creation call in the same session — not a prefix or an id from a prior session.
  2. If the record is legacy (no owner), re-create the task so it is stamped with the current session id.
  3. Check the task still exists via a list/counts call before the runtime thread dereferences it.
  4. Migrate old task records to backfill owner_session_id if legacy data must stay usable.
Defensive patterns

Strategy: validation

Validate before calling

// Validate before use: task must exist and carry an owner session id.
let state = task_manager.snapshot().await;
let ok = state.tasks.get(&task_id)
    .is_some_and(|t| t.owner_session_id.is_some());
anyhow::ensure!(ok, "task {task_id} missing or legacy ownerless; re-create it");
let task = task_manager.get_task_for_active_runtime(&task_id).await?;

Try / catch

match task_manager.get_task_for_active_runtime(&id).await {
    Ok(task) => task,
    Err(e) if e.to_string().contains("Task not found") => {
        // fail closed: re-enqueue the work instead of retrying the stale id
        reenqueue_work().await?
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: A runtime thread references a task id that was pruned, belongs to a rotated store, or predates owner tracking (owner_session_id is None). Passing a prefix is not supported here — only exact ids resolve.

Common situations: Upgrading from an older version whose persisted task records lack owner_session_id; session restore picking up legacy active tasks; referencing a task id after its record was garbage-collected.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/7c8a101f0faa74c4. Report an issue: GitHub.