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
- Provide install_command as a single non-empty string containing the full command
- Join multiple commands into one string (e.g. with &&) instead of an array
- 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
- Express multi-step installs as one &&-joined string
- Don't copy option types from other backends
- Check TOML types with a linter/schema validator
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
- artifactbundle must be true, false, 1, or 0, got
- install_command cannot be used with filter_bins
- aqua var ` ` must be a string, got
- backend must be a string or table
- brew-cask: : must be a boolean
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)