jdx/mise · error
changed after the operation; left untouched
Error message
changed after the operation; left untouched
What it means
recover_path compares PathState::observe(path) against the recorded 'after' state before restoring; if they differ, the file was modified after the operation completed and recovery refuses to overwrite those newer edits, leaving the file untouched.
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 afd2eddd3a)
Solutions
- Decide which version to keep: if the current edits are wanted, use `recover <operation> --keep-current`.
- If the recorded 'after' content is wanted, revert the live file to it manually, then re-run recovery.
- Stop editors/sync tools touching the file, then immediately retry `mise bootstrap dotfiles recover`.
- Diff the live file against the retained recovery data to choose the correct content.
Example fix
// before: file was edited after the operation, recover refuses $ mise bootstrap dotfiles recover // after: explicitly accept the newer edits $ mise bootstrap dotfiles recover <operation> --keep-current
Defensive patterns
Strategy: try-catch
Try / catch
match recover_entries(state_dir, plan) {
Err(e) if e.to_string().contains("changed after the operation") => {
eprintln!("live file was edited; diff it, then choose --keep-current or restore");
}
other => other?,
} Prevention
- Don't edit dotfiles between an interrupted operation and its recovery.
- Disable file-sync tools (Dropbox/iCloud) for dotfile paths during recovery.
- Run recovery promptly after the interruption.
When it happens
Trigger: recover_path invoked when the live path's observed state (contents/mode) no longer equals the 'after' snapshot captured at operation completion — i.e. something edited the file between the operation and recovery.
Common situations: The user (or an editor/sync tool like Dropbox) changed the dotfile after the failed operation but before running recover; concurrent shells writing the same rc file; recovery run long after the interruption.
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
- {target_raw}: tracked in place; pass `--mode copy` after `mi
- {target_raw}: target is already managed by [dotfiles] edits;
- no matching interrupted operation; `mise bootstrap dotfiles
- select one operation by its identifier before accepting curr
- operation {} still needs attention; its pending record was p
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/50555f330459bf9d.
Report an issue: GitHub.