jdx/mise · error

incoming configuration must be a regular file

Error message

incoming configuration must be a regular file: {}

What it means

Thrown during prospective preflight of incoming history-sync configuration when a git entry being pulled from the shared origin has an invalid mode: it is neither a regular file (100644) nor an executable file (100755). mise refuses to treat symlinks, gitlinks (submodules), or other non-regular objects as installable configuration.

Solutions

  1. Replace the symlink/submodule with a regular file in the shared setup and commit
  2. Remove the offending entry from the shared branch if it should not be distributed
  3. Ask the setup author to commit the actual file contents instead of a link
  4. Pull again after the upstream branch is fixed

Example fix

# before (in shared repo)
ln -s ~/real-config.toml mise.toml && git add mise.toml
# after
rm mise.toml && cp ~/real-config.toml mise.toml && git add mise.toml
Defensive patterns

Strategy: validation

Validate before calling

mode=$(git ls-tree HEAD -- <path> | awk '{print $1}')
[[ "$mode" == "100644" || "$mode" == "100755" ]] || echo "replace symlink/submodule at <path> with a regular file"

Prevention

When it happens

Trigger: The incoming branch/commit records a config path as a symlink (120000), submodule gitlink (160000), or other exotic mode while prospective() enumerates files to install.

Common situations: A teammate committed a symlinked config file; a submodule was accidentally added inside the shared config directory; the shared repo contains a directory entry where a file was expected.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/aac9982b93b4a8c5. Report an issue: GitHub.

Appendix: source

Thrown at src/system/history/sync/preflight.rs:51

    if incoming.is_empty() {
        return Ok(tracked.clone());
    }
    let paths: BTreeSet<PathBuf> = incoming.keys().cloned().collect();
    let candidates = crate::config::config_files_with_incoming(&roots.config_dir, &paths);
    // Validate every body we will write, even a conf.d file not selected
    // by this machine's explicit MISE_GLOBAL_CONFIG_FILE override.
    let mut incoming_files = ConfigMap::new();
    for (path, object) in &incoming {
        // The config directory also carries template and other sources.
        if !candidates.contains(path) && crate::env::MISE_GLOBAL_CONFIG_FILE.as_ref() != Some(path)
        {
            continue;
        }
        let Some((mode, oid)) = object else {
            continue;
        };
        if mode != "100644" && mode != "100755" {
            bail!(
                "incoming configuration must be a regular file: {}",
                path.display()
            );
        }
        let body = String::from_utf8(repo.cat_object(oid)?)?;
        let parsed = MiseToml::for_history_preflight(&body, path)?;
        if parsed
            .history_config()
            .is_some_and(|history| history.origin.is_some())
        {
            bail!(
                "incoming history.origin is machine-local configuration; remove it from the shared setup and use `mise dot origin set` on this machine ({})",
                path.display()
            );
        }
        incoming_files.insert(
            path.clone(),
            Arc::new(parsed) as Arc<dyn crate::config::config_file::ConfigFile>,

View on GitHub (pinned to 533346cc37)