jdx/mise · error

install_command must be a non-empty string

Error message

install_command must be a non-empty string

What it means

The SPM backend's optional 'install_command' option must be a string. If the option is present but is not a string (e.g. an array, boolean, or table), the backend cannot build a shell command and throws this error. Despite the message, this variant is specifically the wrong-type case.

Solutions

  1. Provide install_command as a single non-empty string containing the full command
  2. Join multiple commands into one string (e.g. with &&) instead of an array
  3. Remove the option to fall back to the default source build

Example fix

# before
install_command = ["swift", "build"]
# after
install_command = "swift build -c release"
Defensive patterns

Strategy: type-guard

Validate before calling

# ensure install_command is a string in TOML
# valid: install_command = "swift build"
# invalid: install_command = ["swift", "build"]

Type guard

fn is_nonempty_string(v: &toml::Value) -> bool {
    v.as_str().map(|s| !s.trim().is_empty()).unwrap_or(false)
}

Prevention

When it happens

Trigger: Defining install_command as a non-string TOML value (array, inline table, boolean) in an spm tool's options; the option is read in install_command() invoked via source_install_command.

Common situations: Trying to express a multi-command install as a TOML array; accidental TOML structure mistakes; copying config from a different tool backend with different option types.

Related errors


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

Appendix: source

Thrown at src/backend/spm.rs:142

                .filter_map(|v| v.as_str().map(|s| s.trim().to_string()))
                .filter(|s| !s.is_empty())
                .collect(),
            toml::Value::String(s) => s
                .split(',')
                .map(|s| s.trim().to_string())
                .filter(|s| !s.is_empty())
                .collect(),
            _ => return None,
        };
        if bins.is_empty() { None } else { Some(bins) }
    }

    fn install_command(&self) -> eyre::Result<Option<String>> {
        let Some(value) = self.values.raw().opts.get("install_command") else {
            return Ok(None);
        };
        let Some(command) = value.as_str() else {
            bail!("install_command must be a non-empty string");
        };
        let command = command.trim();
        if command.is_empty() {
            bail!("install_command must be a non-empty string");
        }
        Ok(Some(command.to_string()))
    }

    fn source_install_command(&self) -> eyre::Result<Option<String>> {
        let command = self.install_command()?;
        if command.is_some() && self.filter_bins().is_some() {
            bail!("install_command cannot be used with filter_bins");
        }
        Ok(command)
    }
}

#[async_trait]

View on GitHub (pinned to 533346cc37)