jdx/mise · error

[dotfiles]: duplicate OCI file path

Error message

[dotfiles]: duplicate OCI file path {path:?}

What it means

add_file rejects a second file entry at the same OCI path when its contents or mode differ from the first — that would produce an ambiguous/conflicting layer. If contents and mode are identical the duplicate is silently accepted. This protects against contradictory [dotfiles] entries mapping to the same target.

Solutions

  1. Remove or merge the duplicate [dotfiles] entry so each target path is declared once.
  2. If both entries are intentional, make their contents and mode identical so the duplicate is idempotent.
  3. Check target normalization (~ expansion, backslash conversion) to find entries that collide only after normalization.

Example fix

// before — same target, different contents
[dotfiles."~/.zshrc"]
mode = "content"
contents = "A"
[dotfiles."/home/u/.zshrc"]
mode = "content"
contents = "B"

// after — single entry
[dotfiles."~/.zshrc"]
mode = "content"
contents = "B"
Defensive patterns

Strategy: validation

Validate before calling

# ensure identical contents+mode if a target must appear twice
# grep all [dotfiles] entries and diff duplicated ones before building

Prevention

When it happens

Trigger: build_dotfiles_layer processing multiple [dotfiles] entries (or multiple sources for one entry) whose normalized OCI target paths collide with different contents or file modes.

Common situations: Two config entries with equivalent targets after normalization ("~/.zshrc" vs "/root/.zshrc"); a file copied from source in one entry and re-added with mode "content" and different text; changed file mode bits between entries.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at src/oci/builder.rs:1114

#[derive(Default)]
struct DotfilesLayerEntries {
    files: IndexMap<String, (Vec<u8>, u32)>,
    dirs: IndexSet<String>,
}

type DotfilesLayerFile = (String, Vec<u8>, u32);
type DotfilesLayerFiles = Vec<DotfilesLayerFile>;
type DotfilesLayerDirs = Vec<String>;

impl DotfilesLayerEntries {
    fn add_file(&mut self, path: String, contents: Vec<u8>, mode: u32) -> Result<()> {
        if self.dirs.contains(&path) {
            bail!("[dotfiles]: duplicate OCI path {path:?} as both file and directory");
        }
        if let Some((existing_contents, existing_mode)) = self.files.get(&path) {
            if existing_contents != &contents || *existing_mode != mode {
                bail!("[dotfiles]: duplicate OCI file path {path:?}");
            }
            return Ok(());
        }
        self.files.insert(path, (contents, mode));
        Ok(())
    }

    fn add_dir(&mut self, path: String) -> Result<()> {
        if self.files.contains_key(&path) {
            bail!("[dotfiles]: duplicate OCI path {path:?} as both file and directory");
        }
        self.dirs.insert(path);
        Ok(())
    }

    fn into_layer_inputs(self) -> (DotfilesLayerFiles, DotfilesLayerDirs) {
        let files = self
            .files

View on GitHub (pinned to 533346cc37)