{"record":{"id":"66a5e3990a5fee3a","repo":"nikivdev/code","slug":"ambiguous-recipe-selector","errorCode":null,"errorMessage":"ambiguous recipe selector","messagePattern":"ambiguous recipe selector","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/recipe.rs","lineNumber":637,"sourceCode":"    if normalized.is_empty() {\n        bail!(\"empty recipe selector\")\n    }\n\n    if let Some(recipe) = recipes.iter().find(|r| r.id == normalized) {\n        return Ok(recipe);\n    }\n\n    let lowered = normalized.to_ascii_lowercase();\n    let exact_name: Vec<&Recipe> = recipes\n        .iter()\n        .filter(|r| r.name.to_ascii_lowercase() == lowered)\n        .collect();\n    if exact_name.len() == 1 {\n        return Ok(exact_name[0]);\n    }\n    if exact_name.len() > 1 {\n        ambiguous_selector_error(selector, &exact_name)?;\n        bail!(\"ambiguous recipe selector\")\n    }\n\n    let contains: Vec<&Recipe> = recipes\n        .iter()\n        .filter(|r| {\n            r.id.to_ascii_lowercase().contains(&lowered)\n                || r.name.to_ascii_lowercase().contains(&lowered)\n        })\n        .collect();\n    if contains.len() == 1 {\n        return Ok(contains[0]);\n    }\n    if contains.is_empty() {\n        bail!(\"no recipe matched '{}'\", selector);\n    }\n    ambiguous_selector_error(selector, &contains)?;\n    bail!(\"ambiguous recipe selector\")\n}","sourceCodeStart":619,"sourceCodeEnd":655,"githubUrl":"https://github.com/nikivdev/code/blob/a747e741ae92c09071d0ae946ab48488adcff1ce/src/recipe.rs#L619-L655","documentation":"When no recipe id equals the selector, `select_recipe` falls back to case-insensitive exact-name matching; if more than one recipe shares that name, it prints the candidate list to stderr (via `ambiguous_selector_error`) and bails. The library never guesses between equal-priority matches.","triggerScenarios":"Selecting by display name (not id) when two or more recipes in the loaded scope have the same (case-insensitively equal) name — e.g. two scopes each defining a recipe named 'build'.","commonSituations":"Duplicate recipe names across global and project recipe directories; copied recipe files where the name field wasn't changed; team recipes imported under the same name as a personal recipe.","solutions":["Use the unique recipe id instead of the display name (ids are listed on stderr and by the list command)","Rename one of the colliding recipes so names are unique","Restrict the scope (global vs project) so only one matching recipe is loaded","Deduplicate duplicated recipe files in the recipe directories"],"exampleFix":"// before: two recipes named \"Build\"\n$ f recipe run build\nrecipe selector 'build' matched multiple recipes:\n  - build.global (Build)\n  - build.web (Build)\nambiguous recipe selector\n// after\n$ f recipe run build.web   # select by unique id","handlingStrategy":"validation","validationCode":"// preflight: ensure the name resolves to exactly one recipe before selecting\nfn unique_name<'a>(recipes: &'a [Recipe], name: &str) -> Option<&'a Recipe> {\n    let lowered = name.to_ascii_lowercase();\n    let m: Vec<&Recipe> = recipes.iter().filter(|r| r.name.to_ascii_lowercase() == lowered).collect();\n    if m.len() == 1 { Some(m[0]) } else { None } // None => select by id instead\n}","typeGuard":null,"tryCatchPattern":"match run_recipe(opts) {\n    Err(e) if e.to_string().contains(\"ambiguous recipe selector\") => {\n        eprintln!(\"name matched multiple recipes; re-run with a unique recipe id (see stderr list)\");\n        std::process::exit(2);\n    }\n    other => other,\n}","preventionTips":["Always script against recipe ids, never display names","Keep recipe name fields unique across global and project scopes","Rename copied recipe files and update their name metadata","On ambiguity, read the stderr list — it names every colliding id"],"tags":["ambiguity","recipe-selector","cli"],"backgroundTag":"ambiguous-selector-match","analyzedSha":"a747e741ae92c09071d0ae946ab48488adcff1ce","analyzedAt":"2026-09-01T22:43:55.719Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}