Hmbown/CodeWhale · error · anyhow::Error

Ambiguous task prefix '{}': matches {} tasks

Error message

Ambiguous task prefix '{}': matches {} tasks

What it means

Task id resolution matched two or more visible ids with the given prefix, so the reference is ambiguous and the call is rejected rather than guessing. Exact id matches short-circuit before prefix matching, so this only fires for non-exact prefixes.

Source

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

) -> 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))
}

fn summarize_text(text: &str, limit: usize) -> String {
    let take = limit.saturating_sub(3);

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Add more hex characters from the displayed id until it is unique; the error reports how many tasks matched.
  2. Use the complete 21-char id when scripting, since abbreviations are only a convenience for humans.
  3. When surfacing ids in your own UI, show enough characters (>= 8) to keep prefixes unique in practice.

Example fix

// before
let id = resolve_task_id(&tasks, prefix)?; // 'task_1' ambiguous

// after
// grow the prefix until it is unambiguous
let mut p = prefix.to_string();
loop {
    let hits = tasks.keys().filter(|k| k.starts_with(&p)).count();
    if hits == 1 { break; }
    if hits == 0 { bail!("no such task"); }
    p.push(next_char_from_known_id(&p));
}
let id = resolve_task_id(&tasks, &p)?;
Defensive patterns

Strategy: validation

Validate before calling

fn prefix_is_unique(tasks: &HashMap<String, TaskRecord>, prefix: &str) -> bool {
    tasks.keys().filter(|id| id.starts_with(prefix)).count() == 1
}

Prevention

When it happens

Trigger: Passing a short prefix like 'task_a' or a 2-3 hex-char fragment that is a prefix of several task ids, e.g. after many tasks accumulate sharing the 'task_' stem; using the first characters of two ids that differ only late in the string.

Common situations: Long-lived task stores with hundreds of records; scripts that abbreviate ids to a fixed short length; UIs that display truncated ids which users then transcribe.

Related errors


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