jdx/mise · error
command wrapper names collide on macOS after case normalizat
Error message
command wrapper names collide on macOS after case normalization: {name:?} What it means
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.
Source
Thrown at src/shims.rs:1143
}
}
fn validate_wrapper_name(name: &str) -> Result<()> {
if name.is_empty() || name == "." || name == ".." || name.contains('/') || name.contains('\\') {
bail!("invalid command wrapper name: {name:?}");
}
if cfg!(windows) && name.contains('.') {
bail!("command wrapper names cannot contain dots on Windows: {name:?}");
}
Ok(())
}
fn validate_wrapper_names<'a>(names: impl IntoIterator<Item = &'a String>) -> Result<()> {
let mut normalized = HashSet::new();
for name in names {
validate_wrapper_name(name)?;
if cfg!(macos) && !normalized.insert(name.to_lowercase()) {
bail!("command wrapper names collide on macOS after case normalization: {name:?}");
}
}
Ok(())
}
/// Resolve the mise executable that Unix symlink shims should target.
///
/// Snap exposes applications through `/snap/bin`, where each command is a symlink to the
/// `snap` dispatcher. That dispatcher identifies the application from argv[0], so invoking it
/// through a mise shim named `node`, `python`, etc. runs the snap CLI instead of mise. Point Snap
/// shims at the payload beneath its refresh-stable `current` symlink instead. For other package
/// managers, retain the PATH-visible executable so their stable launcher survives upgrades.
pub(crate) fn mise_bin_for_shims() -> PathBuf {
env::var_path("SNAP")
.as_deref()
.and_then(|snap| snap_mise_bin(&env::MISE_BIN, snap))
.unwrap_or_else(|| file::which_no_shims("mise").unwrap_or(env::MISE_BIN.clone()))
}View on GitHub (pinned to afd2eddd3a)
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
Example fix
# before (mise.toml) [wrappers] Docker = "docker compose" docker = "docker" # after [wrappers] Docker = "docker compose" DockerCompose = "docker compose"
Defensive patterns
Strategy: validation
Validate before calling
// Before syncing wrappers on macOS
let names: Vec<&str> = wrappers.keys().map(|s| s.as_str()).collect();
if cfg!(macos) {
let mut seen = std::collections::HashSet::new();
for n in &names {
if !seen.insert(n.to_lowercase()) {
panic!("wrapper names collide case-insensitively on macOS: {n}");
}
}
} Type guard
fn wrapper_names_unique_ci(names: &[String]) -> bool {
let seen: std::collections::HashSet<String> =
names.iter().map(|n| n.to_lowercase()).collect();
seen.len() == names.len()
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- No executable found for configured tool: {bin_name} The inst
- {bin_name} is a mise bin however it is not currently active.
- `defaults {display}` failed: {}
- `defaults {}` failed: {}
- ditto failed copying {} to {}
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/d191f5564c7d8ad3.
Report an issue: GitHub.