jdx/mise · error

managed file paths '{previous}' and '{path}' normalize to th

Error message

managed file paths '{previous}' and '{path}' normalize to the same target '{}'

What it means

While merging `[bootstrap.files]` across config layers, two entries within the SAME config layer produced the same absolute normalized target path. `absolute_target()` canonicalizes the declared path (resolving relative paths and normalization), so distinct-looking keys like "etc/app.conf" and "./etc/app.conf" — or "etc/app.conf" plus a trailing-slash/dot-segment variant — collapse to one target; the per-layer `layer_paths` IndexMap detects the collision (managed_files.rs:344-351). Across different layers, later declarations override earlier ones by design; only same-layer duplicates error.

Source

Thrown at src/system/managed_files.rs:349

fn merged_files_from_config(
    config: &Config,
) -> Result<IndexMap<PathBuf, (ManagedFileTomlConfig, PathBuf)>> {
    let mut merged: IndexMap<PathBuf, (ManagedFileTomlConfig, PathBuf)> = IndexMap::new();
    // Config files are ordered from highest to lowest precedence. Preserve the
    // first declaration of a target so a parent or global layer cannot replace
    // the nearer project declaration.
    for cf in config.config_files.values() {
        if let Some(bootstrap) = cf.bootstrap_config() {
            let mut layer_paths = IndexMap::new();
            let base = cf
                .get_path()
                .parent()
                .unwrap_or_else(|| Path::new("."))
                .to_path_buf();
            for (path, file) in bootstrap.files {
                let target = absolute_target(&path)?;
                if let Some(previous) = layer_paths.insert(target.clone(), path.clone()) {
                    bail!(
                        "managed file paths '{previous}' and '{path}' normalize to the same target '{}'",
                        target.display()
                    );
                }
                merged.entry(target).or_insert_with(|| (file, base.clone()));
            }
        }
    }
    Ok(merged)
}

fn directories_from_config(config: &Config) -> Result<Vec<ManagedDirectoryRequest>> {
    let mut merged: IndexMap<PathBuf, ManagedDirectoryTomlConfig> = IndexMap::new();
    for cf in config.config_files.values() {
        if let Some(bootstrap) = cf.bootstrap_config() {
            let mut layer_paths = IndexMap::new();
            for (path, directory) in bootstrap.directories {
                let target = absolute_target(&path)?;

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Deduplicate the two entries — delete one; they manage the same file, so one fully-specified entry is enough
  2. Pick a single spelling convention (absolute paths, or relative to the config file) for the whole table
  3. If both entries were intentional because you wanted different states per layer, move them into different config layers (e.g. project mise.toml vs ~/.config/mise/mise.toml) where merge-by-override applies instead of erroring

Example fix

# before (same file, two spellings, one layer)
[bootstrap.files."/etc/app.conf"]
content = "a"
[bootstrap.files."/etc/./app.conf"]
content = "b"

# after
[bootstrap.files."/etc/app.conf"]
content = "a"
Defensive patterns

Strategy: validation

Validate before calling

// Same-layer duplicate check before mise sees it (per config file)
use std::collections::HashSet;
use std::path::{Path, Component};
fn norm(p: &Path) -> Path { p.components().collect::<Vec<Component>>().into_iter().collect() }
let mut seen = HashSet::new();
for key in bootstrap_files_keys(config_file) {
    let t = absolute(base_dir, norm(&key));
    assert!(seen.insert(t), "duplicate file target in this config file");
}

Prevention

When it happens

Trigger: One mise.toml containing e.g. `[bootstrap.files."/etc/app.conf"]` and `[bootstrap.files."/etc/./app.conf"]`, or relative variants like `files.cfg` and `./files.cfg` that both resolve against the config file's parent dir to the same absolute path. TOML duplicate keys that are literally identical are rejected by the TOML parser itself, so this error is specifically about paths that differ as strings but normalize identically.

Common situations: Appending a file entry generated by a script that sometimes prefixes `./`; mixing absolute and relative spellings of the same path; normalizing differences introduced by copying config between projects; symlinky path spellings like `/var/../etc/x`.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/c59bdb7c9b28f7dc. Report an issue: GitHub.