{"record":{"id":"8cc8567ac247f1f2","repo":"jdx/mise","slug":"changed-after-it-was-protected-nothing-more-wa","errorCode":null,"errorMessage":"{} changed after it was protected; nothing more was changed","messagePattern":"(.+?) changed after it was protected; nothing more was changed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/replay.rs","lineNumber":614,"sourceCode":"            if removed {\n                let affected = display_path(&step.path);\n                scope.with_operation(|op| {\n                    op.affected.push(affected.clone());\n                    if let Some(bits) = bits {\n                        op.directory_modes.insert(affected.clone(), bits);\n                    }\n                    op.directories.push(affected);\n                });\n                touched.push(step.path.clone());\n            }\n            continue;\n        }\n        // a file created or changed since the verified sample was never\n        // protected: stop here; what was already written is recorded below\n        // and undo can reverse it\n        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),","sourceCodeStart":596,"sourceCodeEnd":632,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/replay.rs#L596-L632","documentation":"When restoring a step, replay compares the file's current object against the object captured at protection time. If they differ, the file changed after the protective checkpoint was taken, so replay stops before doing more damage; already-applied steps are recorded for undo.","triggerScenarios":"A planned step's target path was modified (or created) between the protective checkpoint verification and the moment that step is applied — repo.restored_object_at(&live,...) != current_object(repo, path).","commonSituations":"Editor autosave, formatter-on-save, or a build process touches the file mid-replay; user edits files while a long replay is running.","solutions":["Stop processes that modify the working tree and re-run the replay","Re-take the plan/checkpoint so protection reflects current content, then apply","Use undo (`mise history undo`) to reverse the partially applied steps, then retry cleanly"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// verify file is stable immediately before applying\nlet h1 = hash_file(path)?;\nstd::thread::sleep(Duration::from_millis(200));\nlet h2 = hash_file(path)?;\nassert_eq!(h1, h2, \"{} is being modified concurrently\", path);","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"changed after it was protected\") => {\n        // already-applied steps are undoable\n        run_undo();\n    }\n    other => other?,\n}","preventionTips":["Disable editor autosave/formatters while a replay runs","Don't edit files during long replays; run undo afterward if partial work occurred","Apply plans on a quiet tree"],"tags":["history","replay","race-condition","partial-apply"],"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-14T00:17:10.932Z"}