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
- List tasks (list_tasks / the task list view) and copy the full id or a longer prefix from the current store.
- 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.
- 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
- Resolve ids once at input time and carry the full id through the call chain.
- When surfacing task ids, display the full 21-char form or a clearly truncated one with copy support.
- Remember visibility filters: a hidden task is 'not found' even though its file exists.
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
- Ambiguous task prefix '{}': matches {} tasks
- skill '{name}' is not installed at {}
- Task prompt cannot be empty
- Invalid preallocated task id: expected task_<16hex>
- Task id already exists: {}
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/5a23334c90346cbb.
Report an issue: GitHub.