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

  1. Review the current file content and decide whether to keep it: `mise dot recover <operation> --keep-current`.
  2. If the newer edit should be discarded, restore the intended content manually (or from the recovery store) and re-run recover.
  3. Back up the current file (cp to a safe path) before retrying recovery so the newer edits are preserved somewhere.
  4. 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

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


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 { .. }) && &current == 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)