jdx/mise · error

{} changed during recovery; left untouched

Error message

{} changed during recovery; left untouched

What it means

recover_step restores a file's pre-image after a partially applied sync step. Before restoring it checks the live content still equals the pending 'object' and the permissions equal the desired mode; if the file was modified concurrently, recovery refuses to overwrite it and leaves the file untouched.

Source

Thrown at src/system/history/sync/apply.rs:666

    }
    if !req.automatic {
        info!("history: applied {written} incoming change(s)");
    }
    let outcome = ApplyOutcome {
        written,
        held: held.len(),
        configuration,
    };
    Ok(outcome)
}

fn recover_step(repo: &crate::system::history::shadow::HistoryRepo, step: &Step) -> Result<()> {
    let current = live_object(repo, &step.path)?;
    if current == step.before && live_permissions(&step.path)? == step.before_mode {
        return Ok(());
    }
    if current != step.pending.object || live_permissions(&step.path)? != step.desired_mode {
        bail!(
            "{} changed during recovery; left untouched",
            display_path(&step.path)
        );
    }
    match &step.before {
        Some((mode, oid)) => {
            #[cfg(unix)]
            let bits = {
                use std::os::unix::fs::PermissionsExt;
                step.permissions.as_ref().map(|p| p.mode() & 0o777)
            };
            #[cfg(not(unix))]
            let bits = None;
            replay::write_path_with_mode(repo, &step.path, mode, oid, bits)?;
        }
        None => replay::remove(&step.path)?,
    }
    if !step.path.is_symlink()

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Review the file's current contents, then manually restore or discard changes and re-run the pull.
  2. Stop the process holding/writing the file and retry recovery.
  3. Commit or back up the concurrent edit before re-running sync.
Defensive patterns

Strategy: try-catch

Validate before calling

let current = live_object(repo, &step.path)?;
if current == step.pending.object {
    // safe to recover
}

Try / catch

match apply_result {
    Err(e) if e.to_string().contains("changed during recovery") => {
        // inspect and manually reconcile the file, then retry
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling apply (or recovery) while the target file is being edited; the live file's bytes or mode differ from step.pending.object / step.desired_mode at recovery time.

Common situations: An editor or another process saved the file between apply and recovery; a partially applied pull left the file in a state a running app modified; permissions changed via chmod.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/d6b180ba270ea19d. Report an issue: GitHub.