jdx/mise · error

Check shell must not be empty

Error message

Check shell must not be empty

What it means

Each doctor check runs through a shell. The shell comes from the check's `shell` field or the global default_inline_shell setting; after splitting into program+args, if there is no program (empty shell vector) or the program is blank, run_check bails because there is nothing to execute the check command with.

Solutions

  1. Set an explicit non-empty `shell` on the check, e.g. `shell = "bash -c"`.
  2. Fix the default shell setting (check SHELL env var / default_shell setting) so default_inline_shell() yields a real program.
  3. Ensure the shell string contains a program name, not only whitespace.

Example fix

// before
[[doctor.checks]]
name = "build"
run = "make"
shell = ""

// after
[[doctor.checks]]
name = "build"
run = "make"
shell = "bash -c"
Defensive patterns

Strategy: validation

Validate before calling

// ensure a shell program is available before running doctor checks
let shell = check.shell.clone().or_else(|| std::env::var("SHELL").ok());
assert!(shell.as_deref().map_or(false, |s| !s.trim().is_empty()));

Prevention

When it happens

Trigger: A check with `shell = ""` or `shell = " "`, or Settings::get().default_inline_shell() returning an empty/blank-first-element command when no per-check shell is set.

Common situations: Setting an empty `shell` in project doctor config; a corrupted or unset default_shell setting (e.g. SHELL env var empty in minimal CI containers) leading to an empty default inline shell.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at src/cli/doctor/project.rs:246

    let timeout = check
        .timeout
        .as_deref()
        .map(crate::duration::parse_duration)
        .transpose()?
        .unwrap_or(Duration::from_secs(10));
    if timeout.is_zero() {
        bail!("Check timeout must be greater than zero");
    }
    let mut shell = match &check.shell {
        Some(shell) => crate::path::split_shell_command(shell)?,
        None => Settings::get().default_inline_shell()?,
    };
    Settings::get().maybe_no_profile(&mut shell);
    let Some((program, args)) = shell
        .split_first()
        .filter(|(program, _)| !program.trim().is_empty())
    else {
        bail!("Check shell must not be empty");
    };
    CmdLineRunner::new(program)
        .cmd_body_args(args, &check.run)
        .current_dir(
            check
                .dir
                .as_ref()
                .map(|dir| root.join(crate::file::replace_path(dir)))
                .unwrap_or_else(|| root.to_path_buf()),
        )
        .env_clear()
        .envs(environment)
        .with_timeout(timeout)
        .output_isolated(64 * 1024)
        .await
}

/// Subscribe to a shutdown signal unless the process inherited it as ignored.

View on GitHub (pinned to 533346cc37)