{"record":{"id":"5e2e1c31a08edf0a","repo":"jdx/mise","slug":"incoming-setup-deletes-required-source","errorCode":null,"errorMessage":"incoming setup deletes required source {}","messagePattern":"incoming setup deletes required source (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/sync/preflight.rs","lineNumber":149,"sourceCode":"/// Required source files must exist in the complete proposed write set or\n/// already be available locally. A queued deletion is not an available source.\npub(super) fn sources(repo: &HistoryRepo, tracked: &TrackedSet, plans: &[PathPlan]) -> Result<()> {\n    let roots = Roots::current();\n    for source in &tracked.required_sources {\n        let planned = plans\n            .iter()\n            .find(|plan| roots.locate(&plan.branch_path).path() == Some(source.as_path()));\n        match planned.and_then(|plan| plan.apply.as_ref()) {\n            Some(Some((mode, oid))) => {\n                if mode != \"100644\" && mode != \"100755\" {\n                    bail!(\n                        \"required source is not a regular file: {}\",\n                        source.display()\n                    );\n                }\n                repo.cat_object(oid)?;\n            }\n            Some(None) => bail!(\n                \"incoming setup deletes required source {}\",\n                source.display()\n            ),\n            None if !source.exists() && !has_incoming_child(&roots, source, plans) => {\n                bail!(\n                    \"incoming setup is missing required source {}\",\n                    source.display()\n                );\n            }\n            None => {}\n        }\n    }\n    Ok(())\n}\n\nfn has_incoming_child(roots: &Roots, path: &Path, plans: &[PathPlan]) -> bool {\n    plans.iter().any(|plan| {\n        plan.apply.as_ref().is_some_and(Option::is_some)","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/history/sync/preflight.rs#L131-L167","documentation":"Thrown in sources() when the incoming setup explicitly deletes a source file (recorded as Some(None): path exists in the base tree but is removed in the incoming tree) that the batch still requires. Allowing it would leave a required dependency missing after the batch is applied.","triggerScenarios":"The incoming commit removes a file that other incoming configuration lists as a required source; sources() encounters Some(None) for that path.","commonSituations":"A teammate refactored the shared setup, deleting a file that another file in the same setup still depends on; a partial migration left dangling requirements.","solutions":["Restore the deleted file in the shared branch or update the dependent configuration to stop requiring it","Commit the consistency fix upstream and re-pull","Coordinate with the setup author so deletions and requirements land together"],"exampleFix":"# before: incoming commit runs `git rm required/env.sh` while another file requires it\n# after: keep the file, or update the requiring config's sources to drop required/env.sh","handlingStrategy":"validation","validationCode":"# ensure no required source is deleted in the incoming diff\ngit diff --name-only --diff-filter=D base..incoming | while read -r f; do\n  grep -q \"$f\" required-sources.txt && echo \"deleted but required: $f\"\ndone","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When refactoring the shared setup, delete files and their dependent declarations in the same commit","Grep for references to a file before `git rm`-ing it","Review deletion diffs in setup PRs carefully"],"tags":["history-sync","git","preflight"],"backgroundTag":"resource-not-found","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"}