can1357/oh-my-pi · error · io::Error
backing up {} might destroy source; {} not moved
Error message
backing up {} might destroy source; {} not moved What it means
Raised by handle_two_paths when `mv --backup=numbered/simple` (BackupMode::Simple) would overwrite the backup copy of the source with the source itself. If the target path is exactly what the source's backup file would be named (source + backup suffix), performing the move-and-back-up would first back the target up over the source, destroying it. The library refuses with `backing up 'X' might destroy source; 'Y' not moved` (note the deliberate double space, matching GNU coreutils) and moves nothing.
Source
Thrown at crates/pi-builtins/src/mv.rs:568
}
}
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(host: &mut Host, source: &Path, target: &Path, opts: &Options) -> MvResult<()> {
if opts.backup == BackupMode::Simple && source_is_target_backup(source, target, &opts.suffix) {
return Err(
io::Error::new(
io::ErrorKind::NotFound,
format!(
"backing up {} might destroy source; {} not moved",
target.quote(),
source.quote()
),
)
.into(),
);
}
let source_fs = host.resolve(source);
let target_fs = host.resolve(target);
if source_fs.symlink_metadata().is_err() {
return Err(if path_ends_with_terminator(source) {
MvError::CannotStatNotADirectory(source.quote().to_string()).into()
} else {View on GitHub (pinned to 9690622007)
Solutions
- Rename the source to a target that is not source+suffix, or use a different --suffix value
- Drop the --backup / --b=simple flag for this invocation since you do not need a backup when the name is already backup-shaped
- Check the command's arguments for glob expansion pairing a file with its backup twin (ls to confirm both exist)
- Use numbered backups (--backup=numbered) if you genuinely need both copies preserved
Example fix
// before mv --b=simple config.yaml config.yaml~ // error: backing up 'config.yaml~' might destroy source; 'config.yaml' not moved // after mv --b=simple config.yaml config.yaml.bak // or: mv config.yaml config.yaml~ (no --backup flag)
Defensive patterns
Strategy: validation
Validate before calling
// refuse the dangerous pairing before invoking mv
const suffix = "~";
if (useSimpleBackup && dest === src + suffix) {
throw new Error(`refusing: backing up '${dest}' might destroy source '${src}'`);
} Try / catch
try {
await run("mv", ["--b=simple", src, dest]);
} catch (err) {
if (String(err.stderr).includes("might destroy source")) {
// retry without backup mode or with a different suffix
await run("mv", [src, dest]);
} else throw err;
} Prevention
- Never let globs pair a file with its own backup name in a single mv invocation
- Use a distinctive --suffix that cannot collide with real filenames
- Prefer --backup=numbered when both source and backup must survive
- Dry-run loop bodies with `echo mv ...` before executing bulk renames
When it happens
Trigger: Running `mv --b=simple foo foo~` (with default suffix `~`), i.e. mv foo foo~, or any invocation where source_is_target_backup(source, target, suffix) is true: the target equals source plus the configured --suffix, while simple backup mode is active.
Common situations: Shell globs or loops that accidentally pair a file with its own backup name (`mv $f $f~` in a cleanup loop); scripted editor-style backups where suffix logic collides with the actual filename; users accustomed to `cp --backup` trying the same pattern with mv.
Related errors
- 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}
- cannot overwrite directory {0} with non-directory
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/8a26add7273c2f1d.
Report an issue: GitHub.