{"record":{"id":"c3a0257789345e0c","repo":"Hmbown/CodeWhale","slug":"ambiguous-task-prefix-matches-tasks","errorCode":null,"errorMessage":"Ambiguous task prefix '{}': matches {} tasks","messagePattern":"Ambiguous task prefix '(.+?)': matches (.+?) tasks","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/task_manager.rs","lineNumber":2447,"sourceCode":") -> Result<String> {\n    let visible = |record: &TaskRecord| {\n        owner_session_id.is_none_or(|owner_session_id| {\n            record.owner_session_id.as_deref() == Some(owner_session_id)\n        })\n    };\n    if tasks.get(id_or_prefix).is_some_and(visible) {\n        return Ok(id_or_prefix.to_string());\n    }\n    let matches = tasks\n        .iter()\n        .filter(|(id, record)| id.starts_with(id_or_prefix) && visible(record))\n        .map(|(id, _)| id)\n        .cloned()\n        .collect::<Vec<_>>();\n    match matches.len() {\n        0 => bail!(\"Task not found: {id_or_prefix}\"),\n        1 => Ok(matches[0].clone()),\n        _ => bail!(\n            \"Ambiguous task prefix '{}': matches {} tasks\",\n            id_or_prefix,\n            matches.len()\n        ),\n    }\n}\n\nfn resolve_task_id(tasks: &HashMap<String, TaskRecord>, id_or_prefix: &str) -> Result<String> {\n    resolve_task_id_visible_to(tasks, id_or_prefix, None)\n}\n\nfn summarize_json(value: &Value) -> Option<String> {\n    let text = serde_json::to_string(value).ok()?;\n    Some(summarize_text(&text, TIMELINE_SUMMARY_LIMIT))\n}\n\nfn summarize_text(text: &str, limit: usize) -> String {\n    let take = limit.saturating_sub(3);","sourceCodeStart":2429,"sourceCodeEnd":2465,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/task_manager.rs#L2429-L2465","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Add more hex characters from the displayed id until it is unique; the error reports how many tasks matched.","Use the complete 21-char id when scripting, since abbreviations are only a convenience for humans.","When surfacing ids in your own UI, show enough characters (>= 8) to keep prefixes unique in practice."],"exampleFix":"// before\nlet id = resolve_task_id(&tasks, prefix)?; // 'task_1' ambiguous\n\n// after\n// grow the prefix until it is unambiguous\nlet mut p = prefix.to_string();\nloop {\n    let hits = tasks.keys().filter(|k| k.starts_with(&p)).count();\n    if hits == 1 { break; }\n    if hits == 0 { bail!(\"no such task\"); }\n    p.push(next_char_from_known_id(&p));\n}\nlet id = resolve_task_id(&tasks, &p)?;","handlingStrategy":"validation","validationCode":"fn prefix_is_unique(tasks: &HashMap<String, TaskRecord>, prefix: &str) -> bool {\n    tasks.keys().filter(|id| id.starts_with(prefix)).count() == 1\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Default to the full id in scripts; abbreviate only in interactive contexts.","Show at least 8 hex chars of the id in UIs to keep prefixes practically unique.","On ambiguity the error names the match count: add characters from the candidates shown in the task list."],"tags":["task","lookup","ambiguity","rust"],"backgroundTag":"ambiguous-identifier","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}