{"record":{"id":"e0359af0d141dda3","repo":"jdx/mise","slug":"copy-link-is-not-a-symbolic-link","errorCode":null,"errorMessage":"copy_link is not a symbolic link: {}","messagePattern":"copy_link is not a symbolic link: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/remote.rs","lineNumber":865,"sourceCode":"    for component in link.parent().unwrap_or_else(|| Path::new(\"\")).components() {\n        if let Component::Normal(component) = component {\n            parent.push(component);\n            let metadata = fs::symlink_metadata(&parent).wrap_err_with(|| {\n                format!(\"copy_link parent does not exist: {}\", parent.display())\n            })?;\n            if metadata.file_type().is_symlink() {\n                bail!(\n                    \"copy_link cannot be nested below a symbolic link: {}\",\n                    link.display()\n                );\n            }\n        }\n    }\n    let path = source.join(link);\n    let metadata = fs::symlink_metadata(&path)\n        .wrap_err_with(|| format!(\"copy_link does not exist: {}\", link.display()))?;\n    if !metadata.file_type().is_symlink() {\n        bail!(\"copy_link is not a symbolic link: {}\", link.display());\n    }\n    Ok(())\n}\n\nfn materialize_link(source: &Path, staged_source: &Path, link: &Path) -> Result<()> {\n    validate_copy_link_path(staged_source, link)\n        .wrap_err_with(|| format!(\"staged copy_link is unsafe: {}\", link.display()))?;\n    let source_link = source.join(link);\n    let staged_link = staged_source.join(link);\n    let target = fs::canonicalize(&source_link)\n        .wrap_err_with(|| format!(\"failed to resolve copy_link {}\", link.display()))?;\n    let parent = staged_link\n        .parent()\n        .expect(\"a validated copy_link has a staged parent\");\n    let original_permissions = make_directory_writable(parent)?;\n    let result = (|| {\n        crate::file::remove_file(&staged_link)?;\n        if target.is_dir() {","sourceCodeStart":847,"sourceCodeEnd":883,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/remote.rs#L847-L883","documentation":"After confirming the copy_link path exists in the source, mise requires that the entry actually be a symbolic link, since the remote staging step re-materializes symlinks. A regular file or directory given as copy_link fails with this error.","triggerScenarios":"copy_link names a path that exists but is a regular file or directory, checked via symlink_metadata in validate_copy_link_path before materialize_link runs.","commonSituations":"Config drift: the entry used to be a symlink but was replaced by a real file after vendoring dependencies, or a checkout option (e.g. git config core.symlinks=false) turned symlinks into plain files.","solutions":["Restore the entry to be a symbolic link (ln -s <target> <path>)","Remove the copy_link entry if the file is now a real file and no longer needs replication","On Windows/checkouts with symlinks disabled, enable symlink support in git and re-checkout"],"exampleFix":"// before: bin/tool is a regular file\ncopy_link = \"bin/tool\"\n// after\nrm bin/tool && ln -s scripts/tool bin/tool","handlingStrategy":"validation","validationCode":"function isSymlink(root, rel) {\n  try { return fs.lstatSync(path.join(root, rel)).isSymbolicLink(); } catch { return false; }\n}","typeGuard":"const isSymlink = (p) => { try { return fs.lstatSync(p).isSymbolicLink(); } catch { return false; } };","tryCatchPattern":"try { await stageRemote(); } catch (e) { if (String(e).includes('copy_link is not a symbolic link')) { recreateSymlinks(); } throw e; }","preventionTips":["Keep git core.symlinks enabled and checkouts symlink-faithful","Verify copy_link targets with lstat (not stat) before deployment","Re-create symlinks after vendoring or dependency rewrites"],"tags":["symlink","validation","remote","path"],"backgroundTag":"invalid-argument-value","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"}