{"record":{"id":"3830b1edeab03ed0","repo":"jdx/mise","slug":"required-source-is-not-a-regular-file","errorCode":null,"errorMessage":"required source is not a regular file: {}","messagePattern":"required source is not a regular file: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/sync/preflight.rs","lineNumber":142,"sourceCode":"    // The repository inventory, not a source or output mentioned by incoming\n    // configuration, determines which files the batch may install.\n    let mut prospective = tracked.clone();\n    prospective.required_sources = declarations.required_sources;\n    Ok(prospective)\n}\n\n/// 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        }","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/history/sync/preflight.rs#L124-L160","documentation":"Thrown in sources() preflight when a source file required by the batch resolves in the incoming plans to a git object whose mode is not a regular/executable file (100644/100755). The required dependency must be a real file; symlinks and gitlinks are rejected.","triggerScenarios":"A required source path has an incoming plan whose apply entry records mode 120000 (symlink) or 160000 (gitlink); sources() cat-checks the blob after the mode test fails.","commonSituations":"Shared setup commits a symlink where a required source file should be; submodule accidentally created at a source path.","solutions":["Replace the symlink/submodule with a regular file at the required source path in the shared repo and commit","Remove the path from the setup's required sources if it is not actually needed","Re-pull after the upstream fix"],"exampleFix":"# before\nln -s templates/env.sh required/env.sh && git add required/env.sh\n# after\ncp templates/env.sh required/env.sh && git add required/env.sh","handlingStrategy":"validation","validationCode":"for src in \"${required_sources[@]}\"; do\n  mode=$(git ls-tree HEAD -- \"$src\" | awk '{print $1}')\n  [[ \"$mode\" == 100644 || \"$mode\" == 100755 ]] || echo \"$src is not a regular file\"\ndone","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Commit required sources as real files, never symlinks/submodules","Review `git ls-files -s` output for source directories before merging","Test the shared setup on a clean clone so missing/odd entries surface early"],"tags":["git","history-sync","preflight"],"backgroundTag":"incompatible-source-type","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"}