uutils/coreutils · error · io::Error
mv-error-backup-might-destroy-source
Error message
mv-error-backup-might-destroy-source
What it means
mv refuses to perform the move when creating the requested backup would overwrite (destroy) the source itself. backup_would_destroy_source detects that the backup target name — destination plus backup suffix — resolves to the source file, so proceeding would clobber the input. It returns NotFound with this message quoting both target and source.
Source
Thrown at src/uu/mv/src/mv.rs:381
}
}
fn parse_paths(files: &[OsString], opts: &Options) -> Vec<PathBuf> {
let paths = files.iter().map(Path::new);
if opts.strip_slashes {
paths
.map(|p| p.components().as_path().to_owned())
.collect::<Vec<PathBuf>>()
} else {
paths.map(ToOwned::to_owned).collect::<Vec<PathBuf>>()
}
}
fn handle_two_paths(source: &Path, target: &Path, opts: &Options) -> UResult<()> {
// `mv` never follows a symlink source, so the guard must not either.
if backup_would_destroy_source(source, target, &opts.suffix, opts.backup, false) {
return Err(io::Error::new(
io::ErrorKind::NotFound,
translate!("mv-error-backup-might-destroy-source", "target" => target.quote(), "source" => source.quote()),
)
.into());
}
let Ok(source_metadata) = source.symlink_metadata() else {
return Err(if path_ends_with_terminator(source) {
MvError::CannotStatNotADirectory(source.quote().to_string()).into()
} else {
MvError::NoSuchFile(source.quote().to_string()).into()
});
};
// `symlink_metadata` does not follow symlinks, so this is equivalent to
// `source.is_dir() && !source.is_symlink()` without the extra `stat` calls.
let source_is_dir = source_metadata.is_dir();
let target_is_dir = match target.symlink_metadata() {
Ok(metadata) if metadata.is_symlink() => fs::canonicalize(target).is_ok_and(|p| p.is_dir()),View on GitHub (pinned to 85295bbf78)
Solutions
- Change the backup suffix (--suffix=SFX or VERSION_CONTROL) so the backup name does not equal the source path
- Check whether source and target are the same file (same inode) with `ls -i` and fix the script's path construction
- Drop --backup/-b if no backup is actually needed
- Quote/normalize paths in scripts to avoid aliasing through symlinks or hardlinks
Example fix
// before mv --suffix=.bak notes.txt notes.txt.bak # backup name == ... // after mv --suffix=.old notes.txt notes.txt.bak
Defensive patterns
Strategy: validation
Validate before calling
# ensure backup name does not collide with the source if [ "$target$suffix" = "$source" ] || [ -e "$target$suffix" ] && [ "$target$suffix" -ef "$source" ]; then echo "backup would destroy source"; exit 1 fi
Try / catch
null
Prevention
- Never derive backup suffixes that equal the source filename
- Check inode equality (ls -i / stat) when paths may alias via hardlinks
- Sanitize path variables in scripts before mv with --backup
When it happens
Trigger: mv --backup (or -b with a suffix/VAR) where source and dest are the same file via different paths or the backup name equals the source path, e.g., `mv --suffix=.orig a a.orig` styles of invocations or hardlink/symlink aliasing between source and backup target.
Common situations: Scripts that build backup suffixes from variables which collide with the source name; moving a file onto a path whose backup suffix name points back at the same inode (hardlink); typos where source and target differ only by the backup suffix.
Related errors
- mv-error-directory-not-empty
- invalid destination path
- could not allocate a unique temp name in destination directo
- mv-error-dangling-symlink
- mv-error-dest-appeared
AI-assisted analysis of uutils/coreutils@85295bbf78 (2026-08-31).
Data as JSON: /api/errors/d2c4eef3a6f4b23f.
Report an issue: GitHub.