jdx/mise · warning
changed after the operation; left untouched
Error message
changed after the operation; left untouched
What it means
Before restoring, recover_path re-observes the live path and requires it to match the recorded post-write snapshot (`after`). If the file changed since the operation completed, restoring would clobber those newer edits, so mise leaves the path untouched and reports it.
Solutions
- Review the current file content and decide whether to keep it: `mise dot recover <operation> --keep-current`.
- If the newer edit should be discarded, restore the intended content manually (or from the recovery store) and re-run recover.
- Back up the current file (cp to a safe path) before retrying recovery so the newer edits are preserved somewhere.
- Re-run `mise dot recover` immediately after resolving, since further changes will re-trigger this guard.
Example fix
// before $ mise dot recover # changed after the operation; left untouched // after cp ~/.zshrc ~/.zshrc.myedit.bak mise dot recover <op> --keep-current # or restore intended content first
Defensive patterns
Strategy: validation
Validate before calling
export function assertUnchangedSinceOperation(beforeHash: string, currentHash: string): void {
if (beforeHash !== currentHash) {
throw new Error('file changed since the operation; back up edits before recovery');
}
} Try / catch
try {
await dotRecover(op);
} catch (e) {
if (/changed after the operation/.test(String(e))) {
backupFile(path); // keep the newer edits
await dotRecover(op, { keepCurrent: true });
} else throw e;
} Prevention
- Close editors and stop sync daemons before recovering
- Back up dotfiles before running recovery
- Run recover promptly after a crash, before other tools touch files
When it happens
Trigger: recover_path is called on a path whose current PathState differs from the stored `after` snapshot — the file was edited (by the user, an editor, another tool, or a sync process) between the original write and the recovery attempt.
Common situations: User edited the dotfile after the interrupted operation before running recover; a dotfiles sync tool (e.g. another machine's push) rewrote the file; the shell rc file was regenerated by another program between crash and recovery.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- changed while preparing recovery; left untouched
- interrupted file recovery needs attention; temporary…
- path(s) conflict (see the plan above); resolve them first
- cannot safely change
- changed while preparing enrollment; concurrent declaration…
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/50555f330459bf9d.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/history/recovery.rs:86
after: Option<&PathState>,
) -> Result<()> {
validate_destination(path)?;
let comparison = tempfile::tempdir()?;
let depth = if matches!(prior, PathSnapshot::Directory { .. }) {
Capture::Shallow
} else {
Capture::Full
};
// Comparing must not leave copies of concurrent user edits in the store.
let current = PathSnapshot::capture_with(comparison.path(), path, depth);
if !matches!(prior, PathSnapshot::Unrecorded { .. }) && ¤t == prior {
return Ok(());
}
let Some(after) = after else {
bail!("write completion was not recorded; inspect the live file before retrying recovery");
};
if PathState::observe(path) != *after {
bail!("changed after the operation; left untouched");
}
// Entry count alone cannot establish a directory's identity. Never
// replace a populated directory on that evidence.
if matches!(after, PathState::Dir { entries, .. } if *entries != 0)
&& !(matches!(prior, PathSnapshot::Directory { .. })
&& matches!(
after,
PathState::Dir {
identity: Some(_),
..
}
))
{
bail!("directory contents cannot be verified safely; left untouched");
}
validate_snapshot(state_dir, prior)?;
if PathState::observe(path) != *after {
bail!("changed while preparing recovery; left untouched");View on GitHub (pinned to 533346cc37)