jdx/mise · error

mise executable not found after bootstrap_command

Error message

mise executable not found after bootstrap_command

What it means

After running the user-configured bootstrap_command on a remote host, mise re-scans for mise executables and tries to identify which binary the bootstrap produced. If the post-bootstrap candidate list is completely empty, no mise executable exists anywhere it looks, and it throws this error.

Solutions

  1. Fix bootstrap_command so it installs mise into a scanned location (PATH, ~/.local/bin, /usr/local/bin, etc.)
  2. Set remote_mise or mise_bin explicitly to the installed path so no post-bootstrap selection is needed
  3. Run the bootstrap command manually over SSH to see why it produced no binary
  4. Check that the remote user's PATH and HOME match the environment discovery scans

Example fix

// before
remote_config.bootstrap_command("curl https://mise.jdx.dev/install.sh | sh")
// after (deterministic install path)
remote_config.bootstrap_command("curl https://mise.jdx.dev/install.sh | MISE_INSTALL_PATH=/usr/local/bin/mise sh")
  .mise_bin("/usr/local/bin/mise")
Defensive patterns

Strategy: validation

Validate before calling

test -x "$MISE_BIN" || which mise || echo 'no mise found'   # run over SSH before bootstrap

Type guard

fn bootstrap_yielded_mise(after: &[String]) -> bool { !after.is_empty() }

Try / catch

if let Err(e) = res { if e.to_string().contains("not found after bootstrap_command") { log_bootstrap_output(); fix_bootstrap(); } }

Prevention

When it happens

Trigger: Running a remote operation that invokes bootstrap_command when the command failed silently, installed mise to a non-standard path not in PATH or the well-known locations, or the remote user's PATH/HOME differs from what discovery scans.

Common situations: bootstrap_command that installs mise for a different user or into a custom prefix; remote login shell rc files that change PATH; bootstrap script failing without propagating its exit code; misconfigured HOME on the remote host.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at src/system/remote.rs:1414

                .get(*candidate)
                .and_then(Option::as_ref)
                .and_then(|identity| identity.version.split_whitespace().next())
                .is_some_and(|version| {
                    version == env!("CARGO_PKG_VERSION")
                        || version == concat!(env!("CARGO_PKG_VERSION"), "-DEBUG")
                })
        })
        .collect::<Vec<_>>();
    if let Some(candidate) =
        select_unique_remote_mise_candidate(version_candidates, "version-matching")?
    {
        return Ok(candidate);
    }
    if let [candidate] = after {
        return Ok(candidate.clone());
    }
    if after.is_empty() {
        bail!("mise executable not found after bootstrap_command");
    }
    bail!(
        "bootstrap_command left multiple unchanged mise executables and the installed path is ambiguous: {}; set remote_mise or mise_bin explicitly",
        after.join(", ")
    )
}

fn select_unique_remote_mise_candidate(
    candidates: Vec<&String>,
    kind: &str,
) -> Result<Option<String>> {
    match candidates.as_slice() {
        [] => Ok(None),
        [candidate] => Ok(Some((*candidate).clone())),
        _ => bail!(
            "bootstrap_command left multiple {kind} mise executables and the installed path is ambiguous: {}; set remote_mise or mise_bin explicitly",
            candidates
                .iter()

View on GitHub (pinned to 533346cc37)