Hmbown/CodeWhale · error · anyhow::Error

Task not found: {id}

Error message

Task not found: {id}

What it means

In the cancel path, resolve_task_id_visible_to already returned a full id computed from the same locked state.tasks map, and get_mut(&id) then failed. Because both lookups run under the same mutex on the same map, this branch is a defensive invariant guard, not an expected outcome: it would only fire if the resolver and the map disagreed (internal state corruption). Users should never see it under normal operation.

Source

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

        self.get_task_for_active_runtime(task_id).await?;
        self.cancel_task(task_id).await
    }

    async fn cancel_task_visible_to(
        &self,
        id_or_prefix: &str,
        owner_session_id: Option<&str>,
    ) -> Result<TaskCancellation> {
        let mut state = self.state.lock().await;
        let id = resolve_task_id_visible_to(&state.tasks, id_or_prefix, owner_session_id)?;
        let now = Utc::now();

        let mut cancel_running = false;
        let disposition = {
            let task = state
                .tasks
                .get_mut(&id)
                .ok_or_else(|| anyhow!("Task not found: {id}"))?;
            match task.status {
                TaskStatus::Queued => {
                    task.status = TaskStatus::Canceled;
                    task.lifecycle_seq = task.lifecycle_seq.saturating_add(1);
                    task.ended_at = Some(now);
                    task.duration_ms = Some(0);
                    push_timeline_entry(
                        task,
                        TaskTimelineEntry {
                            timestamp: now,
                            kind: "canceled".to_string(),
                            summary: "Task canceled before execution".to_string(),
                            detail_path: None,
                        },
                    );
                    state.queue.retain(|queued_id| queued_id != &id);
                    TaskCancelDisposition::Forced
                }

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Treat it as an internal invariant failure: capture logs and the persisted task state file.
  2. Restart the session; if it reproduces, inspect the persisted tasks store for corruption.
  3. Report it upstream with the cancel request's id_or_prefix and the task list.
Defensive patterns

Strategy: try-catch

Try / catch

let cancellation = match task_manager.cancel_task_visible_to(prefix, Some(session)).await {
    Ok(c) => c,
    Err(e) if e.to_string().contains("Task not found") => {
        // invariant guard: resolver just resolved this id under the same lock.
        // Log and surface as an internal error; do not blind-retry.
        tracing::error!("cancel invariant violation: {e:#}");
        return Err(e);
    }
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: Effectively unreachable through public APIs: it requires the task map to change between resolve_task_id_visible_to and get_mut while state is locked, e.g. memory corruption or a logic bug where the resolver returns an id not present in the map.

Common situations: If observed at all, it indicates a bug in TaskManager or corrupted persisted task state loaded at startup.

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/3fb2a7946259f549. Report an issue: GitHub.