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

  1. Rename one of the conflicting wrapper names in your mise.toml/command wrapper config so they differ by more than case
  2. Check the error message for the offending name and search your config files (mise.toml, .tool-versions) for case-variants of it
  3. If the collision comes from a tool's wrapper definitions, update or remove that tool so its wrappers no longer clash
  4. 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

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


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/d191f5564c7d8ad3. Report an issue: GitHub.