{"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/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/sync/preflight.rs#L124-L160","documentation":"The sync batch declares certain files as required sources (inputs that other managed files are derived from or depend on). Before applying, mise verifies each required source is available as a regular file in the incoming write set. If the incoming tree entry exists but has a non-regular git mode (symlink, submodule, etc.), mise bails because a required input must be a real, readable file blob.","triggerScenarios":"In `sources`, a required source path matches a plan whose apply entry is `Some(Some((mode, oid)))` with mode not in {100644, 100755}. Triggered when a required source file was committed as a symlink (120000) or submodule (160000) in the incoming branch.","commonSituations":"Someone symlinked a template/source file into the repo instead of copying it; a source path became a submodule after a refactor; legacy repo content contains gitlinks where blobs are expected.","solutions":["In the shared repo, replace the symlink/submodule at the source path with a real regular file and commit.","If the source intentionally lives elsewhere, copy its content into the repository at the required path.","Remove the file from the required-sources declarations if it is no longer a genuine input."],"exampleFix":"// before\nln -s ../../../templates/shellrc .mise/sources/shellrc && git add .mise/sources/shellrc  # 120000\n// after\ncp ../../../templates/shellrc .mise/sources/shellrc && git add .mise/sources/shellrc      # 100644","handlingStrategy":"validation","validationCode":"const entry = planFor(sourcePath)?.apply;\nif (entry && entry.mode !== \"100644\" && entry.mode !== \"100755\") {\n  throw new Error(`required source must be a regular file: ${sourcePath}`);\n}","typeGuard":"const isRegularEntry = (e) => e != null && (e.mode === \"100644\" || e.mode === \"100755\");","tryCatchPattern":"try {\n  await applyIncoming();\n} catch (e) {\n  if (String(e.message).startsWith(\"required source is not a regular file\")) {\n    console.error(\"Commit the source as a real file in the shared repo, not a symlink/submodule.\");\n  } else throw e;\n}","preventionTips":["Commit required source files (templates/inputs) as regular blobs, never symlinks.","Run a pre-push check on tree modes for source directories.","When refactoring sources, verify required-source declarations still point at regular files."],"tags":["git","sync","symlink","validation"],"backgroundTag":"incompatible-source-type","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}