uutils/coreutils · error · io::Error
mv-error-inter-device-move-failed
Error message
mv-error-inter-device-move-failed
What it means
In rename_file_fallback, when the destination is a symlink, mv first removes it before copying the real file. If that remove fails, the error is re-wrapped with the localized inter-device move message including from, to and the underlying OS error, preserving the original io::ErrorKind. It indicates the cross-device rename fallback could not even clear the existing destination symlink.
Source
Thrown at src/uu/mv/src/mv.rs:1442
}
// Preserve ownership (uid/gid) from the source
let _ = preserve_ownership(from, to);
}
Ok(())
}
fn rename_file_fallback(
from: &Path,
to: &Path,
#[cfg(unix)] hardlink_tracker: Option<&mut HardlinkTracker>,
#[cfg(unix)] hardlink_scanner: Option<&HardlinkGroupScanner>,
) -> io::Result<()> {
// Remove existing target file if it exists
if to.is_symlink() {
fs::remove_file(to).map_err(|err| {
let inter_device_msg = translate!("mv-error-inter-device-move-failed", "from" => from.quote(), "to" => to.quote(), "err" => err);
io::Error::new(err.kind(), inter_device_msg)
})?;
} else if to.exists() {
// For non-symlinks, just remove the file without special error handling
fs::remove_file(to)?;
}
// Check if this file is part of a hardlink group and if so, create a hardlink instead of copying
#[cfg(unix)]
{
if let (Some(tracker), Some(scanner)) = (hardlink_tracker, hardlink_scanner) {
use crate::hardlink::HardlinkOptions;
let hardlink_options = HardlinkOptions::default();
if let Some(existing_target) =
tracker.check_hardlink(from, to, scanner, &hardlink_options)
{
// Create a hardlink to the first moved file instead of copying
fs::hard_link(&existing_target, to)?;
fs::remove_file(from)?;View on GitHub (pinned to 85295bbf78)
Solutions
- Read the wrapped `err` for the real cause (EPERM/EACCES/EROFS) and fix permissions or remount read-write.
- Remove the destination symlink manually, then re-run mv.
- Check for immutable flags (`lsattr`, `chattr -i`) or MAC policy denials in audit logs.
- Verify you own the destination directory (sticky bit requires ownership of the link).
Example fix
// before mv bigfile /mnt/ro/link // dest on read-only mount // after sudo mount -o remount,rw /mnt && mv bigfile /mnt/link
Defensive patterns
Strategy: try-catch
Validate before calling
let meta = dest.symlink_metadata()?;
if meta.file_type().is_symlink() {
// test unlinkability up front
if std::fs::remove_file(dest).is_err() {
eprintln!("cannot remove existing dest symlink; fix perms first");
}
} Type guard
fn is_symlink(p: &Path) -> bool {
p.symlink_metadata().map(|m| m.file_type().is_symlink()).unwrap_or(false)
} Try / catch
match mv_result {
Err(e) => eprintln!("mv fallback failed removing dest symlink: {e} (kind={:?})", e.kind()),
Ok(_) => {}
} Prevention
- Ensure you own destination directories with sticky bits
- Avoid read-only/immutable destinations (check lsattr, mount flags)
- Pre-remove conflicting symlinks before scripted moves
When it happens
Trigger: `mv file dest_symlink` where rename fails (EXDEV/cross-device), the fallback tries fs::remove_file(dest_symlink), and the unlink fails — e.g. destination is on a read-only or protected mount, sticky-bit directory not owned by the user, or the link disappeared/was replaced concurrently.
Common situations: Moving onto a symlink in a /tmp-like sticky directory owned by another user; destination filesystem mounted read-only; immutable attribute (chattr +i) on the destination; SELinux/AppArmor denials on unlink.
Related errors
- mv-error-dangling-symlink
- mv-error-permission-denied
- Too many levels of symbolic links
- invalid destination path
- could not allocate a unique temp name in destination directo
AI-assisted analysis of uutils/coreutils@85295bbf78 (2026-08-31).
Data as JSON: /api/errors/3fd62ca83a6c3589.
Report an issue: GitHub.