jdx/mise · error

managed file paths ' ' and ' ' normalize to the same target

Error message

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

What it means

Thrown while merging managed file declarations across config layers when two syntactically different paths normalize (via `absolute_target`) to the same absolute target file. Allowing it would make which declaration wins order-dependent, so the library rejects the ambiguity, reporting both original paths and the shared normalized target.

Solutions

  1. Use one canonical (usually absolute) path for the target across all layers.
  2. Compute the normalized target (`absolute_target`) for both reported paths and see why they collide.
  3. Remove the duplicate declaration or give the second file a distinct target path.
  4. Audit generated/template configs for path-building logic that can emit equivalent-but-different strings.

Example fix

# before
[bootstrap.files."app/config.conf"]      # layer A (relative)
[bootstrap.files."/etc/app/config.conf"] # layer B (absolute)

# after
[bootstrap.files."/etc/app/config.conf"] # single canonical path
Defensive patterns

Strategy: validation

Validate before calling

import os
seen = {}
for p in all_file_paths:
  t = os.path.realpath(os.path.abspath(p))
  if t in seen: raise SystemExit(f'{seen[t]} and {p} normalize to {t}')
  seen[t] = p

Type guard

function normalize(p) { return path.resolve(p); }
function uniqueTargets(paths) {
  const s = new Set(paths.map(normalize)); return s.size === paths.length;
}

Prevention

When it happens

Trigger: Declaring the same file via a relative path in one layer and an absolute path in another (e.g. `config.conf` vs `/full/path/config.conf`); paths differing only by redundant `./` segments or symlink-resolving components that canonicalize to the same target.

Common situations: Configs generated by tooling that emits relative paths while hand-written layers use absolute ones; after changing working-directory assumptions, relative paths now resolve onto absolute ones declared elsewhere; duplicated entries across included config files.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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

Appendix: source

Thrown at src/system/managed_files.rs:427

    // the nearer project declaration.
    for cf in 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();
            let origin = ResourceOrigin {
                config: cf.get_path().to_path_buf(),
                config_root: cf.config_root(),
                environment: crate::config::environments_for_config_path(cf.get_path()),
                source: None,
            };
            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()
                    );
                }
                let mut file_origin = origin.clone();
                file_origin.source = file
                    .source
                    .as_deref()
                    .map(|source| resolve_source_path(&base, source));
                merged
                    .entry(target)
                    .or_insert_with(|| (file, base.clone(), file_origin));
            }
        }
    }
    Ok(merged)
}

View on GitHub (pinned to 533346cc37)