{"record":{"id":"d191f5564c7d8ad3","repo":"jdx/mise","slug":"command-wrapper-names-collide-on-macos-after-case","errorCode":null,"errorMessage":"command wrapper names collide on macOS after case normalization: {name:?}","messagePattern":"command wrapper names collide on macOS after case normalization: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/shims.rs","lineNumber":1143,"sourceCode":"    }\n}\n\nfn validate_wrapper_name(name: &str) -> Result<()> {\n    if name.is_empty() || name == \".\" || name == \"..\" || name.contains('/') || name.contains('\\\\') {\n        bail!(\"invalid command wrapper name: {name:?}\");\n    }\n    if cfg!(windows) && name.contains('.') {\n        bail!(\"command wrapper names cannot contain dots on Windows: {name:?}\");\n    }\n    Ok(())\n}\n\nfn validate_wrapper_names<'a>(names: impl IntoIterator<Item = &'a String>) -> Result<()> {\n    let mut normalized = HashSet::new();\n    for name in names {\n        validate_wrapper_name(name)?;\n        if cfg!(macos) && !normalized.insert(name.to_lowercase()) {\n            bail!(\"command wrapper names collide on macOS after case normalization: {name:?}\");\n        }\n    }\n    Ok(())\n}\n\n/// Resolve the mise executable that Unix symlink shims should target.\n///\n/// Snap exposes applications through `/snap/bin`, where each command is a symlink to the\n/// `snap` dispatcher. That dispatcher identifies the application from argv[0], so invoking it\n/// through a mise shim named `node`, `python`, etc. runs the snap CLI instead of mise. Point Snap\n/// shims at the payload beneath its refresh-stable `current` symlink instead. For other package\n/// managers, retain the PATH-visible executable so their stable launcher survives upgrades.\npub(crate) fn mise_bin_for_shims() -> PathBuf {\n    env::var_path(\"SNAP\")\n        .as_deref()\n        .and_then(|snap| snap_mise_bin(&env::MISE_BIN, snap))\n        .unwrap_or_else(|| file::which_no_shims(\"mise\").unwrap_or(env::MISE_BIN.clone()))\n}","sourceCodeStart":1125,"sourceCodeEnd":1161,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/shims.rs#L1125-L1161","documentation":"mise validates that the command wrapper names being synced as shims do not collide once lowercased. On macOS, the default filesystem (APFS) is case-insensitive, so two wrapper names differing only in case (e.g. `Foo` and `foo`) would map to the same shim file path and silently overwrite each other. mise refuses upfront rather than producing broken, ambiguous shims.","triggerScenarios":"Calling which_shim, ensure_command_wrapper_shims, or sync_command_wrapper_shims on macOS when the set of configured command wrapper names contains two entries that differ only by letter case.","commonSituations":"A user configures two command wrappers whose names differ only in capitalization in mise.toml (e.g. `Docker` and `docker`), or tools contributing wrapper names collide case-insensitively; the error only appears on macOS because Linux filesystems are case-sensitive.","solutions":["Rename one of the conflicting wrapper names in your mise.toml/command wrapper config so they differ by more than case","Check the error message for the offending name and search your config files (mise.toml, .tool-versions) for case-variants of it","If the collision comes from a tool's wrapper definitions, update or remove that tool so its wrappers no longer clash","On Linux this check is skipped; if the names are intentional and you need case-sensitivity, develop on a case-sensitive filesystem"],"exampleFix":"# before (mise.toml)\n[wrappers]\nDocker = \"docker compose\"\ndocker = \"docker\"\n\n# after\n[wrappers]\nDocker = \"docker compose\"\nDockerCompose = \"docker compose\"","handlingStrategy":"validation","validationCode":"// Before syncing wrappers on macOS\nlet names: Vec<&str> = wrappers.keys().map(|s| s.as_str()).collect();\nif cfg!(macos) {\n    let mut seen = std::collections::HashSet::new();\n    for n in &names {\n        if !seen.insert(n.to_lowercase()) {\n            panic!(\"wrapper names collide case-insensitively on macOS: {n}\");\n        }\n    }\n}","typeGuard":"fn wrapper_names_unique_ci(names: &[String]) -> bool {\n    let seen: std::collections::HashSet<String> =\n        names.iter().map(|n| n.to_lowercase()).collect();\n    seen.len() == names.len()\n}","tryCatchPattern":null,"preventionTips":["Keep wrapper names lowercase by convention so case collisions are impossible","Lint mise.toml wrapper sections for case-insensitive duplicates in CI","On macOS, remember APFS is case-insensitive by default and design file-derived names accordingly"],"tags":["macos","shims","config-validation","case-sensitivity"],"backgroundTag":"conflicting-config-options","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}