{"record":{"id":"d6b180ba270ea19d","repo":"jdx/mise","slug":"changed-during-recovery-left-untouched","errorCode":null,"errorMessage":"{} changed during recovery; left untouched","messagePattern":"(.+?) changed during recovery; left untouched","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/sync/apply.rs","lineNumber":796,"sourceCode":"    repo: &crate::system::history::shadow::HistoryRepo,\n    head: &str,\n) -> Result<()> {\n    if crate::config::Settings::get()\n        .history\n        .allow_plaintext_history\n    {\n        return Ok(());\n    }\n    super::files::audit_history(repo, head, &Default::default())\n}\n\nfn recover_step(repo: &crate::system::history::shadow::HistoryRepo, step: &Step) -> Result<()> {\n    let current = live_object(repo, &step.path)?;\n    if current == step.before && live_permissions(&step.path)? == step.before_mode {\n        return Ok(());\n    }\n    if current != step.pending.object || live_permissions(&step.path)? != step.desired_mode {\n        bail!(\n            \"{} changed during recovery; left untouched\",\n            display_path(&step.path)\n        );\n    }\n    match &step.before {\n        Some((mode, oid)) => {\n            #[cfg(unix)]\n            let bits = {\n                use std::os::unix::fs::PermissionsExt;\n                step.permissions.as_ref().map(|p| p.mode() & 0o777)\n            };\n            #[cfg(not(unix))]\n            let bits = None;\n            replay::write_path_with_mode(repo, &step.path, mode, oid, bits)?;\n        }\n        None => replay::remove(&step.path)?,\n    }\n    if !step.path.is_symlink()","sourceCodeStart":778,"sourceCodeEnd":814,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/history/sync/apply.rs#L778-L814","documentation":"During pull application, recover_step restores each path to its pre-image (`step.before`) when a step must be rolled back or reconciled. Before touching the file it re-reads the live object and permissions: if the live content no longer equals the planned pending object/mode, the world no longer matches what the recovery step was computed for, so mise refuses to touch the file and reports it was 'left untouched'. This prevents recovery from clobbering concurrent user edits.","triggerScenarios":"Recovery (rollback or crash-recovery replay of a Step) runs after the file's live content or permissions changed from what the plan captured: `current != step.pending.object || live_permissions != step.desired_mode`. Typically another process modified the dotfile between planning and recovery.","commonSituations":"A user edits a dotfile while a pull is mid-flight and the pull must roll back; a crash leaves a stale journal and by the time recovery replays, the file was edited or replaced; two syncs race on the same file.","solutions":["Inspect the reported path — mise deliberately left it untouched; decide manually whether to keep your edit or let the next `mise dot sync` overwrite it.","Re-run `mise dot sync` after resolving the file; a fresh plan will be computed from current state.","If you want the pulled version, revert your local change (or stash it) and re-run the sync.","Avoid editing dotfiles while a sync is in progress."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// confirm the file is in the expected state before re-running sync\ncurrent=$(sha256sum ~/.config/target | cut -d' ' -f1)\necho \"live hash: $current — if you edited it, resolve manually before syncing\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Don't edit dotfiles while a sync is running.","After a failed/interrupted pull, check the reported path before re-running sync.","Let mise resolve conflicts rather than editing files mid-operation."],"tags":["concurrency","sync","filesystem","safety"],"backgroundTag":"invalid-state-transition","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}