uutils/coreutils · error · io::Error

mv-error-inter-device-move-failed (from, to, err)

Error message

mv-error-inter-device-move-failed (from, to, err)

What it means

In `rename_file_fallback` (the cross-device copy+delete path), if the destination exists as a symlink, `mv` removes it first; if that removal fails, the ENOLINK/EXDEV-style failure is re-raised as `mv-error-inter-device-move-failed` with source, target, and the underlying error embedded.

Solutions

  1. Remove the destination symlink manually and retry the mv
  2. Check permissions/write access on the destination directory (`ls -ld $(dirname to)`)
  3. If the destination is on a read-only mount, remount read-write or choose another target
  4. Copy instead of moving (`cp file to && rm file`) to avoid the remove-then-rename fallback

Example fix

// before
mv data.csv /mnt/usb/data.csv   # /mnt/usb/data.csv is an unremovable symlink
// after
rm /mnt/usb/data.csv && mv data.csv /mnt/usb/data.csv
Defensive patterns

Strategy: retry

Validate before calling

if [ -L "$to" ]; then rm -f "$to" || { echo 'cannot remove dest symlink' >&2; exit 1; }; fi

Try / catch

match mv_result {
    Err(e) if e.to_string().contains("inter-device move failed") => {
        eprintln!("cross-device move failed; check destination symlink permissions: {e}");
    }
    r => r?,
}

Prevention

When it happens

Trigger: Cross-filesystem `mv file /other/mount/file` where `/other/mount/file` is a symlink and `fs::remove_file(to)` fails (permissions, read-only mount, EBUSY).

Common situations: Moving files across mount points / network shares onto stale symlinks, read-only or administratively locked destinations, Windows junction/symlink targets without privileges.

Related errors


AI-assisted analysis of uutils/coreutils@92995feca2 (2026-09-20). Data as JSON: /api/errors/20c0441ec9d5ae27. Report an issue: GitHub.

Appendix: source

Thrown at src/uu/mv/src/mv.rs:1413

        }
        // 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 92995feca2)