{"record":{"id":"0cc11bb4bd1d28ec","repo":"jdx/mise","slug":"brew-cask-completion-executable-is-ambiguous","errorCode":null,"errorMessage":"brew-cask: completion executable '{}' is ambiguous: {}","messagePattern":"brew-cask: completion executable '(.+?)' is ambiguous: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask.rs","lineNumber":4300,"sourceCode":"                .iter()\n                .map(|path| path.display().to_string())\n                .collect::<Vec<_>>()\n                .join(\", \")\n        ),\n    }\n}\n\nfn find_generated_completion_file(root: &Path, executable: &str) -> Result<Option<PathBuf>> {\n    let executable_path = Path::new(executable);\n    let direct = root.join(executable_path);\n    if direct.is_file() {\n        return Ok(Some(direct));\n    }\n    let matches = find_file_artifacts(root, executable_path);\n    match matches.as_slice() {\n        [] => Ok(None),\n        [path] => Ok(Some(path.clone())),\n        _ => bail!(\n            \"brew-cask: completion executable '{}' is ambiguous: {}\",\n            executable,\n            matches\n                .iter()\n                .map(|path| path.display().to_string())\n                .collect::<Vec<_>>()\n                .join(\", \")\n        ),\n    }\n}\n\nfn find_file_artifacts(root: &Path, name: &Path) -> Vec<PathBuf> {\n    let mut matches = WalkDir::new(root)\n        .into_iter()\n        .filter_entry(|entry| entry.file_name() != \"__MACOSX\")\n        .filter_map(|entry| entry.ok())\n        .map(|entry| entry.into_path())\n        .filter(|path| {","sourceCodeStart":4282,"sourceCodeEnd":4318,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/system/packages/brew/cask.rs#L4282-L4318","documentation":"While resolving the executable for a cask's generated-completion stanza, mise first tries the direct path under the staged caskroom, then falls back to find_file_artifacts, which walks the staged tree and matches any file whose relative path ends with the given name. If more than one file matches, mise cannot know which binary to run for completion generation and aborts. This is a deterministic disambiguation guard, not a flaky filesystem issue.","triggerScenarios":"A cask declares a generated completion whose 'executable' is a bare name (e.g. \"pulumi\") and the staged payload contains multiple files whose relative paths end with that name, e.g. both 'bin/pulumi' and 'libexec/pulumi/pulumi' (find_file_artifacts matches relative.ends_with(name) at src/system/packages/brew/cask.rs:4320). It only fires when the direct join root/executable is not itself a file.","commonSituations":"Apps that ship a launcher script plus a same-named real binary in a nested directory; wrapper-and-target layouts common in Go/Rust CLIs; cask stanzas written against an older payload layout that later added a second copy of the binary; __MACOSX entries are already filtered, so remaining duplicates are real payload files.","solutions":["In the cask's generated-completion stanza, give the executable as an unambiguous relative path (e.g. \"bin/pulumi\" instead of \"pulumi\") so the direct root.join() hit at the top of find_generated_completion_file succeeds.","Inspect the staged caskroom payload (the cask's staged directory under the brew prefix) for duplicate matching filenames: the error message lists every candidate path; pick the correct one and use its path relative to the stage root.","If you maintain the cask definition, point the stanza at the exact binary that implements completion generation, and add a test that only one candidate exists.","If you cannot change the stanza, update mise — a newer revision may add precedence rules — or disable the generated-completion stanza for that cask."],"exampleFix":"# cask generated-completion stanza (before)\n\"completions\": { \"generated\": [{ \"executable\": \"pulumi\", \"shell\": \"zsh\" }] }\n# ambiguous: bin/pulumi and libexec/pulumi/pulumi both match\n\n# after — disambiguate with the relative path\n\"completions\": { \"generated\": [{ \"executable\": \"bin/pulumi\", \"shell\": \"zsh\" }] }","handlingStrategy":"validation","validationCode":"# before mise install, check the staged payload for duplicate matches\nEXE=\"pulumi\"; ROOT=\"$(mise where brew-cask:<token> 2>/dev/null || echo /dev/null)\"\nif [ -d \"$ROOT\" ]; then\n  COUNT=$(find \"$ROOT\" -type f -path \"*$EXE\" ! -path \"*__MACOSX*\" | wc -l)\n  [ \"$COUNT\" -gt 1 ] && echo \"ambiguous: pick an exact relative path\" && find \"$ROOT\" -type f -path \"*$EXE\"\nfi","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always specify generated-completion executables as exact relative paths ('bin/tool'), never bare names.","When authoring cask metadata, assert only one payload file ends with the executable name.","Pin cask versions you have tested so payload layout changes cannot surprise a stanza."],"tags":["brew-cask","completions","file-resolution","ambiguity","mise"],"backgroundTag":"ambiguous-file-match","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}