jdx/mise · error
{} appeared after {} was protected; nothing more was changed
Error message
{} appeared after {} was protected; nothing more was changed What it means
When replaying a step that recreates a directory, replay checks the directory's current contents against plan steps. A 'stray' file present inside the directory that is not covered by any step means it appeared after the directory was protected — replay aborts rather than overwrite or orphan it.
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 afd2eddd3a)
Solutions
- Remove or account for the stray file (it's named in the message), then re-run the replay
- Stop the process creating files inside the target directory and retry
- Re-plan so the new file is captured in a fresh protective checkpoint
Defensive patterns
Strategy: validation
Validate before calling
// ensure the directory to restore contains no untracked newcomers
let stray: Vec<_> = std::fs::read_dir(dir)?
.filter_map(|e| e.ok())
.filter(|e| !planned_paths.contains(&e.path()))
.collect();
assert!(stray.is_empty(), "unplanned files present: {:?}", stray); Prevention
- Check target directories for new files (logs, caches) before replaying
- Stop processes that write into directories slated for recreation
- Re-plan after any new files appear so they're captured
When it happens
Trigger: directory_contents() finds inside.appeared non-empty: a file exists inside step.path now that was absent from the protective snapshot and has no corresponding plan step.
Common situations: A build/cache directory gets a new file (log, artifact, .DS_Store) between checkpoint and replay; watchers or sync clients drop files into restored directories mid-run.
Related errors
- these paths keep changing while being protected; nothing was
- {} changed after it was protected; nothing more was changed
- {} cannot be recreated as a directory: something else is the
- the refreshed plan has conflicts; nothing was changed
- declined; nothing was changed
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/5a0bebc68feae6ff.
Report an issue: GitHub.