{"record":{"id":"bb346950a4d2a806","repo":"jdx/mise","slug":"local-files-were-saved-after-enrollment-planning","errorCode":null,"errorMessage":"local files were saved after enrollment planning; retry pull","messagePattern":"local files were saved after enrollment planning; retry pull","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/sync/apply.rs","lineNumber":592,"sourceCode":"                .as_deref()\n                .ok_or_else(|| eyre::eyre!(\"local history disappeared\"))?;\n            // The operation's own protective commit may save the unsaved\n            // versions that an explicit --take-remote decision will replace.\n            // Permit only changes at those preflighted paths; any other saved\n            // change requires a fresh complete plan.\n            let restore_planned = ready\n                .iter()\n                .map(|step| {\n                    Ok(crate::system::history::shadow::Overlay {\n                        path: step.pending.branch_path.clone(),\n                        object: repo.object_at(local, &step.pending.branch_path)?,\n                    })\n                })\n                .collect::<Result<Vec<_>>>()?;\n            if repo.compose(&repo.output_tree_of(current)?, &restore_planned)?\n                != repo.output_tree_of(local)?\n            {\n                bail!(\"local files were saved after enrollment planning; retry pull\");\n            }\n            // Do not let a divergent text merge or a stale resolution differ\n            // from the complete tree that will be adopted.\n            for step in &ready {\n                if repo.restored_object_at(tree, &step.pending.branch_path)? != step.pending.object\n                {\n                    bail!(\n                        \"{} changed in the complete repository merge; reconcile again\",\n                        display_path(&step.path)\n                    );\n                }\n            }\n            let candidate = heads\n                .candidate(repo, tree)?\n                .ok_or_else(|| eyre::eyre!(\"setup branch disappeared\"))?;\n            audit_incoming_history(repo, &candidate.commit)?;\n        }\n        // Validate the complete batch again after acquiring the operation","sourceCodeStart":574,"sourceCodeEnd":610,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/history/sync/apply.rs#L574-L610","documentation":"During a setup-adoption pull (enrollment into sharing), mise verifies that the live local tree is exactly the tree that was planned at enrollment time. If composing the planned restore onto the current output tree differs from the actual local tree, files were modified after enrollment planning, so the pull refuses rather than adopting a tree built from stale assumptions.","triggerScenarios":"First `mise dot pull` after enrollment (restore steps pending) where any tracked file's content or mode changed between enrollment planning and the pull — detected by `repo.compose(output_tree_of(current), restore_planned) != output_tree_of(local)`.","commonSituations":"Editing dotfiles after enrollment but before the first pull; a package manager rewrote a tracked config in between; another tool touched enrolled files.","solutions":["Re-run `mise dot pull` to regenerate enrollment planning against the current files (as the message says, retry).","If retries keep failing, re-run the enrollment/setup step so planning matches the live tree.","Avoid editing tracked files between enrollment and the first successful pull."],"exampleFix":"before: edit ~/.bashrc after enrollment planning, then mise dot pull (fails) | after: run mise dot pull so the plan is regenerated against current files","handlingStrategy":"retry","validationCode":"mise dot status  # ensure no edits land between enrollment and the first pull","typeGuard":null,"tryCatchPattern":"mise dot pull || mise dot pull  # bounded retry when the enrollment race is transient","preventionTips":["Don't edit tracked files between enrollment and the first successful pull.","Run the first pull immediately after enrollment, before other tooling touches configs.","Quiesce config-rewriting daemons (theme switchers, package managers) during enrollment."],"tags":["sync","enrollment","race-condition","dotfiles"],"backgroundTag":"checksum-mismatch","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"}