can1357/oh-my-pi · error · io::Error

can't determine symlink type, since it is dangling

Error message

can't determine symlink type, since it is dangling

What it means

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.

Source

Thrown at crates/pi-builtins/src/mv.rs:1131

	{
		let _ = copy_xattrs_if_supported(host, from, to);
	}
	fs::remove_file(host.resolve(from))
}

#[cfg(windows)]
fn rename_symlink_fallback(host: &mut Host, from: &Path, to: &Path) -> io::Result<()> {
	let path_symlink_points_to = fs::read_link(host.resolve(from))?;
	let to_fs = host.resolve(to);
	if path_symlink_points_to.exists() {
		if path_symlink_points_to.is_dir() {
			windows::fs::symlink_dir(&path_symlink_points_to, &to_fs)?;
		} else {
			windows::fs::symlink_file(&path_symlink_points_to, &to_fs)?;
		}
		fs::remove_file(host.resolve(from))
	} else {
		Err(io::Error::new(
			io::ErrorKind::NotFound,
			"can't determine symlink type, since it is dangling",
		))
	}
}

#[cfg(target_os = "wasi")]
fn rename_symlink_fallback(host: &mut Host, _from: &Path, _to: &Path) -> io::Result<()> {
	Err(io::Error::other("your operating system does not support symlinks"))
}

fn rename_dir_fallback(
	host: &mut Host,
	from: &Path,
	to: &Path,
	display_manager: Option<&MultiProgress>,
	verbose: bool,
	#[cfg(unix)] hardlink_tracker: Option<&mut HardlinkTracker>,

View on GitHub (pinned to 9690622007)

Solutions

  1. Restore or recreate the symlink's target so the link is not dangling before moving it
  2. Delete the dangling symlink and create a new one at the destination manually (New-Item -ItemType SymbolicLink)
  3. Move the operation to a Unix environment, where dangling symlinks are moved verbatim without error
  4. If you maintain the library, resolve the link type from the link's own metadata (reparse point flags) instead of probing the target

Example fix

// before (PowerShell)
Move-Item C:\src\link.txt D:\dest\   # link.txt points to missing file
// error: can't determine symlink type, since it is dangling
// after
New-Item -ItemType File -Path C:\src\target.txt | Out-Null  # restore target
Move-Item C:\src\link.txt D:\dest\
Defensive patterns

Strategy: validation

Validate before calling

const { lstatSync, readlinkSync, existsSync } = require("node:fs");
function isDanglingSymlink(p) {
  const st = lstatSync(p, { throwIfNoEntry: false });
  if (!st || !st.isSymbolicLink()) return false;
  return !existsSync(p); // exists() follows links
}
// before a cross-drive move on Windows: if (isDanglingSymlink(link)) repairOrDeleteIt(link);

Try / catch

try {
  await run("mv", [src, dest]);
} catch (err) {
  if (String(err.stderr).includes("dangling")) {
    // recreate the link at destination manually, then remove source link
    const target = readlinkSync(src);
    await run("cmd", ["/c", "mklink", dest, target]);
    await fs.promises.unlink(src);
  } else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/2078d21140e28844. Report an issue: GitHub.