{"record":{"id":"aac9982b93b4a8c5","repo":"jdx/mise","slug":"incoming-configuration-must-be-a-regular-file","errorCode":null,"errorMessage":"incoming configuration must be a regular file: {}","messagePattern":"incoming configuration must be a regular file: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/sync/preflight.rs","lineNumber":51,"sourceCode":"    if incoming.is_empty() {\n        return Ok(tracked.clone());\n    }\n    let paths: BTreeSet<PathBuf> = incoming.keys().cloned().collect();\n    let candidates = crate::config::config_files_with_incoming(&roots.config_dir, &paths);\n    // Validate every body we will write, even a conf.d file not selected\n    // by this machine's explicit MISE_GLOBAL_CONFIG_FILE override.\n    let mut incoming_files = ConfigMap::new();\n    for (path, object) in &incoming {\n        // The config directory also carries template and other sources.\n        if !candidates.contains(path) && crate::env::MISE_GLOBAL_CONFIG_FILE.as_ref() != Some(path)\n        {\n            continue;\n        }\n        let Some((mode, oid)) = object else {\n            continue;\n        };\n        if mode != \"100644\" && mode != \"100755\" {\n            bail!(\n                \"incoming configuration must be a regular file: {}\",\n                path.display()\n            );\n        }\n        let body = String::from_utf8(repo.cat_object(oid)?)?;\n        let parsed = MiseToml::for_history_preflight(&body, path)?;\n        if parsed\n            .history_config()\n            .is_some_and(|history| history.origin.is_some())\n        {\n            bail!(\n                \"incoming history.origin is machine-local configuration; remove it from the shared setup and use `mise bootstrap dotfiles origin set` on this machine ({})\",\n                path.display()\n            );\n        }\n        incoming_files.insert(\n            path.clone(),\n            Arc::new(parsed) as Arc<dyn crate::config::config_file::ConfigFile>,","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/sync/preflight.rs#L33-L69","documentation":"During history-sync preflight, mise validates every incoming configuration file from the shared repository before applying it. Git tree entries carry a mode, and only regular files (modes 100644 and 100755) can be parsed as TOML configuration. If an incoming path is a symlink (120000), submodule (160000), or gitlink, mise refuses to continue rather than silently skipping or misinterpreting it.","triggerScenarios":"A commit in the dotfiles/config repository sets a configuration path (a path matched by is_configuration, e.g. a mise.toml or conf.d file) to a non-blob object: a symlink entry (mode 120000), a git submodule entry (160000), or any other non-regular mode. Raised in `prospective` when iterating planned applies and checking `mode != \"100644\" && mode != \"100755\"`.","commonSituations":"A teammate symlinked mise.toml to a file outside the repo (e.g. `ln -s ~/dotfiles/mise.toml mise.toml`); someone added a git submodule inside the config directory; a tool vendored a directory entry where a config file was expected.","solutions":["Replace the symlink/submodule with a real regular file: `git rm --cached <path>` then copy the target content into the path and commit with mode 100644/100755.","If the config is genuinely shared elsewhere, vendor its contents into the repository instead of linking.","Pull the corrected upstream commit (or reset to a good revision) and rerun the sync/preflight."],"exampleFix":"// before (in the shared repo)\nln -s ~/real-mise.toml mise.toml && git add mise.toml   # mode 120000\n// after\ncp ~/real-mise.toml mise.toml && git add mise.toml      # mode 100644","handlingStrategy":"validation","validationCode":"const mode = treeEntry.mode;\nif (mode !== \"100644\" && mode !== \"100755\") {\n  throw new Error(`incoming configuration must be a regular file: ${path}`);\n}","typeGuard":"function isRegularFileMode(mode: string): boolean {\n  return mode === \"100644\" || mode === \"100755\";\n}","tryCatchPattern":"try {\n  await syncPull();\n} catch (e) {\n  if (String(e.message).includes(\"must be a regular file\")) {\n    console.error(`Fix non-file entry in shared repo: ${e.message}`);\n  } else throw e;\n}","preventionTips":["Never commit symlinks for config paths in the shared dotfiles repository; commit real files.","Avoid adding git submodules inside the config directory.","Add a CI lint that rejects tree entries with modes other than 100644/100755 under config paths."],"tags":["git","config","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"}