{"record":{"id":"15abf26c50be47d1","repo":"jdx/mise","slug":"task-cache-archive-contains-duplicate-paths","errorCode":null,"errorMessage":"task cache archive contains duplicate paths","messagePattern":"task cache archive contains duplicate paths","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/task/task_cache_store.rs","lineNumber":613,"sourceCode":"                    size,\n                },\n                executable: mode & 0o111 != 0,\n                mode,\n                file: temporary,\n            }\n        } else if entry_type == EntryType::Symlink {\n            let target = entry\n                .header()\n                .link_name()\n                .ok_or_else(|| eyre!(\"remote cache symlink is missing its target\"))?\n                .into_owned();\n            validate_cache_symlink_target(&entry_path, &target)?;\n            ArchiveNode::Symlink { mode, target }\n        } else {\n            bail!(\"unsupported task cache archive entry type\");\n        };\n        if nodes.insert(entry_path.clone(), node).is_some() {\n            bail!(\"task cache archive contains duplicate paths\");\n        }\n        let mut parent = entry_path.parent();\n        while let Some(path) = parent {\n            nodes\n                .entry(path.to_path_buf())\n                .or_insert(ArchiveNode::Directory { mode: 0o755 });\n            parent = path.parent();\n        }\n    }\n\n    fn build_directory(\n        path: &Path,\n        nodes: &BTreeMap<PathBuf, ArchiveNode>,\n        directory_uploads: &mut Vec<BlobUpload>,\n    ) -> Result<CacheDigest> {\n        let mut directories = Vec::new();\n        let mut files = Vec::new();\n        let mut symlinks = Vec::new();","sourceCodeStart":595,"sourceCodeEnd":631,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/task/task_cache_store.rs#L595-L631","documentation":"The archive walker builds a map of path → node and refuses duplicates: two entries claiming the same path (e.g. a file and a directory at the same name, or the same file listed twice) would make the cached directory tree ambiguous. The insert returning Some triggers this error.","triggerScenarios":"archive_to_cas encountering two entries with the identical entry_path — e.g. an archive containing both \"a\" as a file and \"a/b\" entries, or a literally duplicated path entry.","commonSituations":"Archives produced by buggy packaging scripts that append twice; tar files where a path first appears as a directory prefix (auto-inserted) and later as a file; non-deterministic build outputs rebuilt mid-archive.","solutions":["Rebuild the output/archive so each path appears exactly once.","Rename one of the conflicting entries (a file cannot share a path with a directory).","Fix the packaging tool to deduplicate or to use distinct directory prefixes (e.g. \"a/\" vs \"a.txt\")."],"exampleFix":"// before\n// entries: [\"out\" (dir), \"out\" (file)]\n// after\n// entries: [\"out\" (dir), \"out/result.txt\" (file)]","handlingStrategy":"validation","validationCode":"fn paths_unique(entries: &[PathBuf]) -> bool {\n    let mut seen = std::collections::HashSet::new();\n    entries.iter().all(|p| seen.insert(p.clone()))\n}","typeGuard":null,"tryCatchPattern":"match commit(&out).await {\n    Err(e) if e.to_string().contains(\"duplicate paths\") => {\n        // deduplicate/rebuild the archive and retry once\n    }\n    r => r?,\n}","preventionTips":["Never append to an archive twice in packaging scripts","Ensure a path is not both a directory prefix and a file entry","Make output generation deterministic before archiving"],"tags":["archive","duplicate-path","task-cache"],"backgroundTag":"invalid-argument-format","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}