jdx/mise · error
expected a `run` command
Error message
expected a `run` command
What it means
Bootstrap hook TOML parsing reached the command specification but found no `run` key: a hook entry must declare what to execute (a run command, string array, or table with run), and this table has none of those, so from_toml rejects it.
Solutions
- Add a `run` string or array of strings to the hook table.
- If the command lives under another key, rename that key to `run`.
- Remove the empty hook table if it's not needed.
Example fix
// before [[hooks]] phase = "pretool" // after [[hooks]] phase = "pretool" run = "mise run prepare"
Defensive patterns
Strategy: validation
Validate before calling
fn hook_has_run(table: &toml::value::Table) -> bool { table.contains_key("run") } Try / catch
match load_hooks(config) {
Err(e) if e.to_string().contains("expected a `run` command") => eprintln!("hook missing run: {}", e),
other => other?,
} Prevention
- Always pair `phase` with a `run` key when defining hooks.
- Don't rename `run` to `command`/`script` — only `run` is accepted.
- Validate hook tables against the mise schema in CI.
When it happens
Trigger: Writing a hook table with only phase (and maybe other metadata) but no `run`, e.g. `[[hooks]] phase = "pretool"` with no run key.
Common situations: Forgot to fill in the command after scaffolding the hook; deleted the run line during editing; renamed the key to something like `command` or `script` by mistake.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- expected `run` to be a string or array of strings
- agent ' ' must set `program
- backend must be a string or table
- collection updates require scalar or array values
- command wrapper for must have a non-blank command
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/a93174165402deaf.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/hooks.rs:96
let Some(phase) = BootstrapHookPhase::parse(phase_raw) else {
let valid = BootstrapHookPhase::iter()
.map(|phase| phase.as_str())
.collect::<Vec<_>>();
bail!(
"unknown bootstrap hook phase {phase_raw:?}; valid phases are: {}",
valid.join(", ")
);
};
let runs = match value {
toml::Value::String(run) => vec![run],
toml::Value::Array(values) => string_array(values, "expected string commands")?,
toml::Value::Table(mut table) => match table.remove("run") {
Some(toml::Value::String(run)) => vec![run],
Some(toml::Value::Array(values)) => {
string_array(values, "expected `run` to contain string commands")?
}
Some(_) => bail!("expected `run` to be a string or array of strings"),
None => bail!("expected a `run` command"),
},
_ => bail!("expected a string, array of strings, or table with `run`"),
};
let hooks = runs
.into_iter()
.filter_map(|run| {
let run = run.trim().to_string();
if run.is_empty() {
warn!("[bootstrap.hooks.{phase}]: empty command, ignoring entry");
None
} else {
Some(Self {
phase,
run,
config_path: config_path.clone(),
})
}
})View on GitHub (pinned to 533346cc37)