{"record":{"id":"c2a73e821f56d1ca","repo":"jdx/mise","slug":"dotfiles-duplicate-oci-file-path-path","errorCode":null,"errorMessage":"[dotfiles]: duplicate OCI file path {path:?}","messagePattern":"\\[dotfiles\\]: duplicate OCI file path (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/oci/builder.rs","lineNumber":1114,"sourceCode":"\n#[derive(Default)]\nstruct DotfilesLayerEntries {\n    files: IndexMap<String, (Vec<u8>, u32)>,\n    dirs: IndexSet<String>,\n}\n\ntype DotfilesLayerFile = (String, Vec<u8>, u32);\ntype DotfilesLayerFiles = Vec<DotfilesLayerFile>;\ntype DotfilesLayerDirs = Vec<String>;\n\nimpl DotfilesLayerEntries {\n    fn add_file(&mut self, path: String, contents: Vec<u8>, mode: u32) -> Result<()> {\n        if self.dirs.contains(&path) {\n            bail!(\"[dotfiles]: duplicate OCI path {path:?} as both file and directory\");\n        }\n        if let Some((existing_contents, existing_mode)) = self.files.get(&path) {\n            if existing_contents != &contents || *existing_mode != mode {\n                bail!(\"[dotfiles]: duplicate OCI file path {path:?}\");\n            }\n            return Ok(());\n        }\n        self.files.insert(path, (contents, mode));\n        Ok(())\n    }\n\n    fn add_dir(&mut self, path: String) -> Result<()> {\n        if self.files.contains_key(&path) {\n            bail!(\"[dotfiles]: duplicate OCI path {path:?} as both file and directory\");\n        }\n        self.dirs.insert(path);\n        Ok(())\n    }\n\n    fn into_layer_inputs(self) -> (DotfilesLayerFiles, DotfilesLayerDirs) {\n        let files = self\n            .files","sourceCodeStart":1096,"sourceCodeEnd":1132,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/oci/builder.rs#L1096-L1132","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Remove or merge the duplicate [dotfiles] entry so each target path is declared once.","If both entries are intentional, make their contents and mode identical so the duplicate is idempotent.","Check target normalization (~ expansion, backslash conversion) to find entries that collide only after normalization."],"exampleFix":"// before — same target, different contents\n[dotfiles.\"~/.zshrc\"]\nmode = \"content\"\ncontents = \"A\"\n[dotfiles.\"/home/u/.zshrc\"]\nmode = \"content\"\ncontents = \"B\"\n\n// after — single entry\n[dotfiles.\"~/.zshrc\"]\nmode = \"content\"\ncontents = \"B\"","handlingStrategy":"validation","validationCode":"# ensure identical contents+mode if a target must appear twice\n# grep all [dotfiles] entries and diff duplicated ones before building","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Declare each [dotfiles] target once; merge contents in one place.","If duplication is intentional, keep contents and file mode identical across entries.","Watch for entries that collide only after ~ expansion or backslash normalization."],"tags":["oci","dotfiles","conflict","duplicate"],"backgroundTag":"file-already-exists","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}