jdx/mise · error

these paths keep changing while being protected; nothing…

Error message

these paths keep changing while being protected; nothing was changed: {}

What it means

After taking the protective checkpoint, mise re-verifies (up to VERIFY_ROUNDS rounds) that every path it is about to touch still matches what was captured. If paths keep appearing/changing between rounds, it cannot guarantee a consistent restore point and aborts. Nothing was changed.

Solutions

  1. Stop the process writing those paths (watcher, dev server, running task), then re-run the rollback.
  2. Exclude the churning paths from the restore scope, or restore a narrower set of stable files.
  3. Check for a concurrent mise history/rollback process and wait for it to finish.
Defensive patterns

Strategy: validation

Validate before calling

fuser -v <target-paths> 2>/dev/null || echo "no writers detected"

Prevention

When it happens

Trigger: A set of target paths is repeatedly found missing or modified across verification rounds in apply_steps, exceeding VERIFY_ROUNDS — e.g. another process writes those files continuously while the rollback is being prepared.

Common situations: A dev server, watcher, build task, or editor actively writing files under the paths being restored; another concurrent mise run touching the same files.

Related errors


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

Appendix: source

Thrown at src/system/history/replay.rs:533

            .checkpoint
            .tree
            .snapshot
            .clone()
            .ok_or_else(|| eyre::eyre!("the protective checkpoint has no content"))?;
        let mut missing = vec![];
        for step in steps.iter().filter(|step| step.action.mutates()) {
            let Some(current) = repo.restored_object_at(&live, &step.tree_path)? else {
                continue;
            };
            if repo.restored_object_at(&before_tree, &step.tree_path)? != Some(current) {
                missing.push(step.path.clone());
            }
        }
        if missing.is_empty() {
            break;
        }
        if round >= VERIFY_ROUNDS {
            bail!(
                "these paths keep changing while being protected; nothing was changed: {}",
                missing
                    .iter()
                    .map(display_path)
                    .collect::<Vec<_>>()
                    .join(", ")
            );
        }
        warn!(
            "history: {} path(s) changed after the protective checkpoint; capturing again",
            missing.len()
        );
        scope.recapture_before(&missing)?;
    }
    let mut touched = vec![];
    // deletions deepest first, then writes shallowest first: a directory is
    // emptied before the file that replaces it is written, and a directory
    // that replaces a file exists before its files are written

View on GitHub (pinned to 533346cc37)