{"record":{"id":"411fc047211b5b40","repo":"jdx/mise","slug":"existing-symbolic-link-conflicts-with-adoption-e","errorCode":null,"errorMessage":"existing symbolic link conflicts with adoption: {entry}","messagePattern":"existing symbolic link conflicts with adoption: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/remote_repository.rs","lineNumber":329,"sourceCode":"                    &format!(\"refs/remotes/origin/{branch}\"),\n                    revision,\n                ],\n            )?;\n        } else if dry_run {\n            miseprintln!(\n                \"Would keep the existing checkout of {origin} at {shown}; --update fast-forwards it\"\n            );\n        }\n        return Ok(destination.to_path_buf());\n    }\n    let nonempty = destination.exists() && destination.read_dir()?.next().is_some();\n    if nonempty {\n        for entry in entries.split('\\0').filter(|s| !s.is_empty()) {\n            let mut ancestor = destination.to_path_buf();\n            for component in Path::new(entry).components() {\n                ancestor.push(component);\n                if ancestor.is_symlink() {\n                    bail!(\"existing symbolic link conflicts with adoption: {entry}\");\n                }\n            }\n            let existing = destination.join(entry);\n            if existing.exists()\n                && (checkout.join(entry).is_symlink()\n                    || !existing.is_file()\n                    || std::fs::read(&existing)? != std::fs::read(checkout.join(entry))?)\n            {\n                bail!(\"existing file conflicts with adoption: {entry}\");\n            }\n        }\n        if dry_run {\n            let new_files = entries\n                .split('\\0')\n                .filter(|s| !s.is_empty())\n                .filter(|entry| !destination.join(entry).exists())\n                .count();\n            miseprintln!(","sourceCodeStart":311,"sourceCodeEnd":347,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/remote_repository.rs#L311-L347","documentation":"When adopting a non-empty destination directory (no `.git` present), install_at walks every path component of every file coming from the transferred repository and rejects the adoption if any ancestor within the existing directory is a symbolic link. Symlinks could redirect the rename of transferred files outside the intended directory, so the operation is treated as unsafe and aborted.","triggerScenarios":"Installing into an existing non-empty directory where any prefix of a transferred file's path (or the file entry path itself) is a symlink — e.g. `~/.config/mise` contains a symlinked subdirectory like `plugins -> ~/dotfiles/plugins` that a transferred entry also writes into.","commonSituations":"Users symlink parts of their config directory into a dotfiles repo; package managers or tools created symlinked subdirectories inside the config path; a symlink loop or link to shared state exists in the tree.","solutions":["Find the offending link: run the command with dry-run/preview, or `find <destination> -type l` to list symlinks under the destination.","Replace the symlink with a real directory (e.g. copy its contents and `rm` the link), or remove the symlink if it is not needed.","Remove the corresponding path from the transferred source repository if that entry should not be transferred at all.","Transfer into a fresh empty directory instead of adopting the existing symlink-laden layout."],"exampleFix":"// before: ~/.config/mise/plugins is a symlink\nls -la ~/.config/mise   # plugins -> ~/dotfiles/plugins\nrm ~/.config/mise/plugins && cp -r ~/dotfiles/plugins ~/.config/mise/plugins\n// after: no symlinks in the path; adoption proceeds","handlingStrategy":"validation","validationCode":"import { execFileSync } from 'child_process';\nconst links = execFileSync('find', [dest, '-type', 'l']).toString().trim();\nif (links.length > 0) throw new Error(`symlinks under destination block adoption:\\n${links}`);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep the global config directory free of symlinks; copy instead of link.","Run `find <dest> -type l` before onboarding an existing directory.","Exclude symlinked subdirectories from the transferred repository layout."],"tags":["git","symlink","security"],"backgroundTag":"path-traversal-blocked","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"}