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

  1. Add a `run` string or array of strings to the hook table.
  2. If the command lives under another key, rename that key to `run`.
  3. 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

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


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)