Hmbown/CodeWhale · error · anyhow::Error

Task not found: {id_or_prefix}

Error message

Task not found: {id_or_prefix}

What it means

Task id resolution: the argument matched neither a full visible task id nor the prefix of any visible task. Resolution first tries an exact key lookup, then prefix matching over ids, optionally filtered by a visibility predicate; zero matches out of both paths produces this error.

Source

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

    id_or_prefix: &str,
    owner_session_id: Option<&str>,
) -> Result<String> {
    let visible = |record: &TaskRecord| {
        owner_session_id.is_none_or(|owner_session_id| {
            record.owner_session_id.as_deref() == Some(owner_session_id)
        })
    };
    if tasks.get(id_or_prefix).is_some_and(visible) {
        return Ok(id_or_prefix.to_string());
    }
    let matches = tasks
        .iter()
        .filter(|(id, record)| id.starts_with(id_or_prefix) && visible(record))
        .map(|(id, _)| id)
        .cloned()
        .collect::<Vec<_>>();
    match matches.len() {
        0 => bail!("Task not found: {id_or_prefix}"),
        1 => Ok(matches[0].clone()),
        _ => bail!(
            "Ambiguous task prefix '{}': matches {} tasks",
            id_or_prefix,
            matches.len()
        ),
    }
}

fn resolve_task_id(tasks: &HashMap<String, TaskRecord>, id_or_prefix: &str) -> Result<String> {
    resolve_task_id_visible_to(tasks, id_or_prefix, None)
}

fn summarize_json(value: &Value) -> Option<String> {
    let text = serde_json::to_string(value).ok()?;
    Some(summarize_text(&text, TIMELINE_SUMMARY_LIMIT))
}

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. List tasks (list_tasks / the task list view) and copy the full id or a longer prefix from the current store.
  2. If you scoped the lookup (resolve_task_id_visible_to with a filter), widen the scope or confirm the task is visible to the calling context.
  3. Prefixes only need to be unambiguous, not complete: 4-6 hex chars usually suffice; if you get 'Ambiguous task prefix' instead, add characters.
Defensive patterns

Strategy: validation

Validate before calling

// Verify a reference resolves before acting on it:
fn task_exists(tasks: &HashMap<String, TaskRecord>, prefix: &str) -> bool {
    tasks.get(prefix).is_some() || tasks.keys().any(|id| id.starts_with(prefix))
}

Try / catch

match resolve_task_id(&tasks, &arg) {
    Ok(id) => act_on(id).await,
    Err(e) if e.to_string().starts_with("Task not found") => {
        let options = tm.list_tasks(Some(10)).await;
        show_picker(options).await // recover by reselecting
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Calling a task-scoped API (status, update, result) with a mistyped id, a stale id from a previous run, a too-short prefix that no longer matches, or an id that exists but is filtered out by the caller's visibility scope.

Common situations: Typing a prefix in a command or tool call; referencing a task from an old session whose files were archived; scope filters (e.g. 'my tasks') hiding the target; ids copied after truncation in a narrow UI.

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/5a23334c90346cbb. Report an issue: GitHub.