jdx/mise · error

conflicting dotfile declarations for {} first: {}

Error message

conflicting dotfile declarations for {}

  first:
    {}

  second:
    {}

What it means

When composing FileRequests from [dotfiles] config, composed_files_from_config detects two declarations for the same target whose modes conflict. Two non-Track declarations for the same destination are incompatible unless both are SymlinkEach (which composes disjoint leaves); anything else — e.g. Track+Copy, Copy+Symlink, SymlinkOnce+SymlinkEach — bails with both declarations' origin descriptions.

Source

Thrown at src/system/files.rs:351

            if let Some(existing) = siblings
                .iter_mut()
                .find(|existing| file_requests_match(config, existing, &request))
            {
                // the same declaration from a later layer: what it says
                // about `enabled`, the policies, and the variants wins, so a
                // local `enabled = false` overrides what
                // it inherited instead of being dropped as a duplicate; what
                // it leaves unsaid stays inherited
                existing.override_from(request);
                continue;
            }
            if let Some(existing) = siblings.iter().find(|existing| {
                existing.mode != FileMode::Track
                    && request.mode != FileMode::Track
                    && (existing.mode != FileMode::SymlinkEach
                        || request.mode != FileMode::SymlinkEach)
            }) {
                bail!(
                    "conflicting dotfile declarations for {}\n\n  first:\n    {}\n\n  second:\n    {}",
                    request.target.display(),
                    existing.origin.conflict_description(),
                    request.origin.conflict_description(),
                );
            }
            siblings.push(request);
        }
    }
    Ok(composed.into_values().flatten().collect())
}

/// Whether a declaration comes from the system or global layers (or a root
/// they compose): the only layers history enrolls files from.
pub(crate) fn declaration_is_global(config: &Config, req: &FileRequest) -> bool {
    track_layer_allowed(&req.origin, &global_composed_roots(config))
}

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Make the two declarations agree: use the same mode (e.g. both SymlinkEach) or merge them into a single entry in one config file.
  2. Change one declaration to mode Track, which observes the file without owning an apply footprint and thus composes with other modes.
  3. Delete the redundant declaration so only one non-Track owner remains for that target.

Example fix

# before: conflicting declarations for ~/.gitconfig
# global mise.toml
[dotfiles."~/.gitconfig"]
source = "~/dotfiles/gitconfig"
# project mise.toml
[dotfiles."~/.gitconfig"]
content = "..."

# after: single owner
[dotfiles."~/.gitconfig"]
source = "~/dotfiles/gitconfig"
Defensive patterns

Strategy: validation

Validate before calling

# find duplicate targets across your mise.toml files
grep -h '^\[dotfiles\.' ~/.config/mise/config.toml ./mise.toml | sort | uniq -d

Prevention

When it happens

Trigger: Calling files_from_config → composed_files_from_config with two [dotfiles] entries resolving to the same target path where at least one is not Track and not both SymlinkEach (e.g. one config file declares a symlink, another declares copy/track for the same destination).

Common situations: The same dotfile path is declared in global config and a project config with different modes (one uses source symlink, another inline content/template/copy); merging the layered configs produces two conflicting declarations for one target.

Related errors


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