{"record":{"id":"5a0bebc68feae6ff","repo":"jdx/mise","slug":"appeared-after-was-protected-nothing-more-w","errorCode":null,"errorMessage":"{} appeared after {} was protected; nothing more was changed","messagePattern":"(.+?) appeared after (.+?) was protected; nothing more was changed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/replay.rs","lineNumber":630,"sourceCode":"        let expected = repo.restored_object_at(&live, &step.tree_path)?;\n        if !same_object(&expected, &current_object(repo, &step.path)?) {\n            bail!(\n                \"{} changed after it was protected; nothing more was changed\",\n                display_path(&step.path)\n            );\n        }\n        let was_directory = step.path.is_dir() && !step.path.is_symlink();\n        let mut empty_dirs = vec![];\n        if was_directory && !matches!(&step.action, Action::Write { mode, .. } if mode == \"040000\")\n        {\n            // replacing or removing a directory removes everything inside\n            // it: a file history would capture must be a step of this plan\n            // (else it appeared after protection); a file history never\n            // covers goes with it, said out loud; empty subdirectories\n            // leave no trace in a snapshot and are recorded for undo\n            let inside = directory_contents(&step.path, steps, tracked)?;\n            if let Some(stray) = inside.appeared.first() {\n                bail!(\n                    \"{} appeared after {} was protected; nothing more was changed\",\n                    display_path(stray),\n                    display_path(&step.path)\n                );\n            }\n            if !inside.uncovered.is_empty() {\n                // only reachable with --force (a type change): say what\n                // goes with the directory that no checkpoint holds\n                warn!(\n                    \"history: {} contains files history does not cover; they are removed with it and cannot be undone: {}\",\n                    display_path(&step.path),\n                    inside\n                        .uncovered\n                        .iter()\n                        .map(display_path)\n                        .collect::<Vec<_>>()\n                        .join(\", \")\n                );","sourceCodeStart":612,"sourceCodeEnd":648,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/replay.rs#L612-L648","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// ensure the directory to restore contains no untracked newcomers\nlet stray: Vec<_> = std::fs::read_dir(dir)?\n    .filter_map(|e| e.ok())\n    .filter(|e| !planned_paths.contains(&e.path()))\n    .collect();\nassert!(stray.is_empty(), \"unplanned files present: {:?}\", stray);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["history","replay","race-condition","directory-restore"],"backgroundTag":"concurrent-modification-detected","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}