jdx/mise · error
appeared after was protected; nothing more was changed
Error message
{} appeared after {} was protected; nothing more was changed What it means
While restoring a directory, mise enumerates its live contents and checks that every file it finds is covered by a step in the plan. A 'stray' file means the directory gained a file after the checkpoint was taken; that file would not be captured by the plan or the undo record, so mise aborts the remaining steps (already-written steps are recorded and undoable).
Solutions
- Stop processes generating files inside the restored directory, then re-run the rollback.
- Move the stray files out of the restore directory if you want them preserved, then retry.
- Narrow the restore scope to individual files instead of the whole directory.
Defensive patterns
Strategy: validation
Validate before calling
find <dir> -newer <checkpoint-marker> -type f # detect files created after protection
Prevention
- Pause file-generating tools (watchers, package managers) before directory restores.
- Keep generated artifacts out of directories you plan to restore wholesale.
- Prefer file-level restore scopes over whole directories when feasible.
When it happens
Trigger: directory_contents() reports an entry in inside.appeared during a directory restore step in apply_steps — a new file exists under step.path that no plan step accounts for.
Common situations: A watcher, package manager, or build task created files (logs, node_modules artifacts, caches) inside the directory between checkpoint and apply.
Related errors
- changed after it was protected; nothing more was changed
- these paths keep changing while being protected; nothing…
- another history operation is running
- another history operation is running
- cannot retain an unreadable plaintext version of newly…
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/5a0bebc68feae6ff.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/history/replay.rs:630
let expected = repo.restored_object_at(&live, &step.tree_path)?;
if !same_object(&expected, ¤t_object(repo, &step.path)?) {
bail!(
"{} changed after it was protected; nothing more was changed",
display_path(&step.path)
);
}
let was_directory = step.path.is_dir() && !step.path.is_symlink();
let mut empty_dirs = vec![];
if was_directory && !matches!(&step.action, Action::Write { mode, .. } if mode == "040000")
{
// replacing or removing a directory removes everything inside
// it: a file history would capture must be a step of this plan
// (else it appeared after protection); a file history never
// covers goes with it, said out loud; empty subdirectories
// leave no trace in a snapshot and are recorded for undo
let inside = directory_contents(&step.path, steps, tracked)?;
if let Some(stray) = inside.appeared.first() {
bail!(
"{} appeared after {} was protected; nothing more was changed",
display_path(stray),
display_path(&step.path)
);
}
if !inside.uncovered.is_empty() {
// only reachable with --force (a type change): say what
// goes with the directory that no checkpoint holds
warn!(
"history: {} contains files history does not cover; they are removed with it and cannot be undone: {}",
display_path(&step.path),
inside
.uncovered
.iter()
.map(display_path)
.collect::<Vec<_>>()
.join(", ")
);View on GitHub (pinned to 533346cc37)