{"record":{"id":"2078d21140e28844","repo":"can1357/oh-my-pi","slug":"can-t-determine-symlink-type-since-it-is-dangling","errorCode":null,"errorMessage":"can't determine symlink type, since it is dangling","messagePattern":"can't determine symlink type, since it is dangling","errorType":"error_code","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/pi-builtins/src/mv.rs","lineNumber":1131,"sourceCode":"\t{\n\t\tlet _ = copy_xattrs_if_supported(host, from, to);\n\t}\n\tfs::remove_file(host.resolve(from))\n}\n\n#[cfg(windows)]\nfn rename_symlink_fallback(host: &mut Host, from: &Path, to: &Path) -> io::Result<()> {\n\tlet path_symlink_points_to = fs::read_link(host.resolve(from))?;\n\tlet to_fs = host.resolve(to);\n\tif path_symlink_points_to.exists() {\n\t\tif path_symlink_points_to.is_dir() {\n\t\t\twindows::fs::symlink_dir(&path_symlink_points_to, &to_fs)?;\n\t\t} else {\n\t\t\twindows::fs::symlink_file(&path_symlink_points_to, &to_fs)?;\n\t\t}\n\t\tfs::remove_file(host.resolve(from))\n\t} else {\n\t\tErr(io::Error::new(\n\t\t\tio::ErrorKind::NotFound,\n\t\t\t\"can't determine symlink type, since it is dangling\",\n\t\t))\n\t}\n}\n\n#[cfg(target_os = \"wasi\")]\nfn rename_symlink_fallback(host: &mut Host, _from: &Path, _to: &Path) -> io::Result<()> {\n\tErr(io::Error::other(\"your operating system does not support symlinks\"))\n}\n\nfn rename_dir_fallback(\n\thost: &mut Host,\n\tfrom: &Path,\n\tto: &Path,\n\tdisplay_manager: Option<&MultiProgress>,\n\tverbose: bool,\n\t#[cfg(unix)] hardlink_tracker: Option<&mut HardlinkTracker>,","sourceCodeStart":1113,"sourceCodeEnd":1149,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-builtins/src/mv.rs#L1113-L1149","documentation":"Windows-only error from rename_symlink_fallback: when mv must re-create a symlink at the destination (inter-device/copy fallback instead of a plain rename), it needs to know whether the link points to a file or a directory to pick symlink_file vs symlink_dir. For a dangling symlink (its target does not exist), that cannot be determined, so the move fails with `can't determine symlink type, since it is dangling` (io::ErrorKind::NotFound). On Unix this case succeeds because symlink(2) copies the link contents verbatim.","triggerScenarios":"On Windows, moving a symlink whose target does not exist across a filesystem/device boundary (or any case where rename fails and rename_symlink_fallback runs): read_link succeeds, but path_symlink_points_to.exists() is false, so neither symlink_dir nor symlink_file can be chosen.","commonSituations":"Moving broken symlinks (targets deleted, moved, or on unmounted drives) between drives on Windows; test fixtures with dangling links; CI checkouts where symlink targets were not materialized.","solutions":["Restore or recreate the symlink's target so the link is not dangling before moving it","Delete the dangling symlink and create a new one at the destination manually (New-Item -ItemType SymbolicLink)","Move the operation to a Unix environment, where dangling symlinks are moved verbatim without error","If you maintain the library, resolve the link type from the link's own metadata (reparse point flags) instead of probing the target"],"exampleFix":"// before (PowerShell)\nMove-Item C:\\src\\link.txt D:\\dest\\   # link.txt points to missing file\n// error: can't determine symlink type, since it is dangling\n// after\nNew-Item -ItemType File -Path C:\\src\\target.txt | Out-Null  # restore target\nMove-Item C:\\src\\link.txt D:\\dest\\","handlingStrategy":"validation","validationCode":"const { lstatSync, readlinkSync, existsSync } = require(\"node:fs\");\nfunction isDanglingSymlink(p) {\n  const st = lstatSync(p, { throwIfNoEntry: false });\n  if (!st || !st.isSymbolicLink()) return false;\n  return !existsSync(p); // exists() follows links\n}\n// before a cross-drive move on Windows: if (isDanglingSymlink(link)) repairOrDeleteIt(link);","typeGuard":null,"tryCatchPattern":"try {\n  await run(\"mv\", [src, dest]);\n} catch (err) {\n  if (String(err.stderr).includes(\"dangling\")) {\n    // recreate the link at destination manually, then remove source link\n    const target = readlinkSync(src);\n    await run(\"cmd\", [\"/c\", \"mklink\", dest, target]);\n    await fs.promises.unlink(src);\n  } else throw err;\n}","preventionTips":["Audit for dangling symlinks (targets missing) before cross-drive moves on Windows","Repair or delete broken links as part of cleanup before moving directories","On Unix, no guard is needed - dangling links move verbatim","Keep symlink targets on the same volume as the links to avoid fallback moves"],"tags":["windows","symlink","mv","dangling-symlink"],"backgroundTag":"dangling-symlink","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}