can1357/oh-my-pi · error · io::Error
inter-device move failed: {} to {}; unable to remove target:
Error message
inter-device move failed: {} to {}; unable to remove target: {err} What it means
Raised in rename_file_fallback during an inter-device (cross-filesystem) move when the fallback first tries to remove an existing target symlink at the destination and that removal fails. The error wraps the original removal error with `inter-device move failed: 'from' to 'to'; unable to remove target: <err>`, preserving the original io::ErrorKind. It signals that the copy-based fallback could not even clear the destination, so the move is aborted.
Source
Thrown at crates/pi-builtins/src/mv.rs:1398
}
fn rename_file_fallback(
host: &mut Host,
from: &Path,
to: &Path,
#[cfg(unix)] hardlink_tracker: Option<&mut HardlinkTracker>,
#[cfg(unix)] hardlink_scanner: Option<&HardlinkGroupScanner>,
) -> io::Result<()> {
let to_fs = host.resolve(to);
// Remove existing target file if it exists
if to_fs.is_symlink() {
fs::remove_file(&to_fs).map_err(|err| {
let inter_device_msg = format!(
"inter-device move failed: {} to {}; unable to remove target: {err}",
from.quote(),
to.quote()
);
io::Error::new(err.kind(), inter_device_msg)
})?;
} else if to_fs.exists() {
// For non-symlinks, just remove the file without special error handling
fs::remove_file(&to_fs)?;
}
// 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 hardlink::HardlinkOptions;
let hardlink_options = HardlinkOptions::default();
if let Some(existing_target) = tracker.check_hardlink(host, from, to, scanner, &hardlink_options)
{
// Create a hardlink to the first moved file instead of copying
fs::hard_link(host.resolve(&existing_target), &to_fs)?;
fs::remove_file(host.resolve(from))?;View on GitHub (pinned to 9690622007)
Solutions
- Fix the underlying removal error shown after 'unable to remove target:' (check permissions/ownership of the destination directory, free space, and read-only status)
- Delete or rename the existing symlink at the destination manually before the move
- Check that the destination filesystem is mounted read-write (mount | grep <dest>) and remount if needed
- Move to a destination path that does not already contain a symlink
Example fix
# before mv /home/user/data.txt /mnt/usb/data.txt # error: inter-device move failed: '/home/user/data.txt' to '/mnt/usb/data.txt'; unable to remove target: Permission denied # after sudo chown $(whoami) /mnt/usb/ # or: rm /mnt/usb/data.txt mv /home/user/data.txt /mnt/usb/data.txt
Defensive patterns
Strategy: try-catch
Validate before calling
const { lstatSync, accessSync, constants, statSync } = require("node:fs");
function canMoveOntoDest(dest) {
const dir = path.dirname(dest);
try { accessSync(dir, constants.W_OK); } catch { return false; }
const st = lstatSync(dest, { throwIfNoEntry: false });
if (st && st.isSymbolicLink()) {
try { return statSync(dest).isFile() || true; } catch { /* dangling link: removable if dir is writable */ }
}
return true;
} Try / catch
try {
await run("mv", [src, dest]);
} catch (err) {
if (String(err.stderr).includes("inter-device move failed")) {
const cause = String(err.stderr).split("unable to remove target:")[1]?.trim();
if (cause?.includes("Permission denied")) {
// fix perms or remove the target symlink, then retry once
await fs.promises.unlink(dest).catch(() => {});
await run("mv", [src, dest]);
} else throw err;
} else throw err;
} Prevention
- Verify the destination directory is writable by the current user before cross-device moves
- Remount read-only destinations read-write before scripted moves
- Clear stale symlinks from destination directories ahead of bulk moves
- Check free space and mount status (ro/rw) of the target volume when moving across devices
When it happens
Trigger: Moving a file across devices (e.g. /home to /mnt/usb) where the destination path is an existing symlink whose fs::remove_file fails - typically due to permissions on the containing directory, a read-only filesystem, or the link being in use; the map_err at mv.rs:1392-1399 attaches the inter-device context.
Common situations: Cross-drive moves onto read-only or full USB/network mounts; destination directory lacking write permission so the existing symlink cannot be unlinked; stale symlinks in target directories owned by another user; Docker/volume mounts where unlink is restricted.
Related errors
- {}: {error}
- cannot stat {0}: No such file or directory
- cannot stat {0}: Not a directory
- {0} and {1} are the same file
- cannot move {0} to a subdirectory of itself, {1}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/2e43e60b10c79c56.
Report an issue: GitHub.