jdx/mise · error

unknown bootstrap hook phase {phase_raw:?}; valid phases are

Error message

unknown bootstrap hook phase {phase_raw:?}; valid phases are: {}

What it means

BootstrapHook::from_toml treats every key under [bootstrap.hooks] as a phase name. Valid phases are pre-packages, post-packages, pre-repos, post-repos, pre-dotfiles, post-dotfiles, pre-defaults, post-defaults, pre-user, post-user, pre-tools, post-tools, and final (parse() normalizes '_' to '-', so pre_packages is also accepted). Any other key bails with this error and the message lists the accepted phases.

Source

Thrown at src/system/hooks.rs:76

impl fmt::Display for BootstrapHookPhase {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BootstrapHook {
    pub phase: BootstrapHookPhase,
    pub run: String,
}

impl BootstrapHook {
    pub fn from_toml(phase_raw: &str, value: toml::Value) -> Result<Vec<Self>> {
        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

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Rename the key to one of the phases enumerated in the error message (pre-packages, post-packages, pre-repos, post-repos, pre-dotfiles, post-dotfiles, pre-defaults, post-defaults, pre-user, post-user, pre-tools, post-tools, final)
  2. Check for singular/plural mistakes (it is pre-packages, not pre-package)
  3. Re-run mise bootstrap to confirm the hooks table parses

Example fix

# before
[bootstrap.hooks]
post-install = "./setup.sh"

# after
[bootstrap.hooks]
post-tools = "./setup.sh"
Defensive patterns

Strategy: validation

Validate before calling

# verify every phase key is known before bootstrap
for p in $(git config -f mise.toml --get-regexp '^bootstrap\.hooks\.' 2>/dev/null | cut -d. -f3- | cut -d' ' -f1); do
  case "$p" in pre-packages|post-packages|pre-repos|post-repos|pre-dotfiles|post-dotfiles|pre-defaults|post-defaults|pre-user|post-user|pre-tools|post-tools|final) ;; *) echo "unknown hook phase: $p";; esac
done

Prevention

When it happens

Trigger: Writing an unknown key under [bootstrap.hooks], e.g. [bootstrap.hooks.pre_package] (singular), [bootstrap.hooks.post-install], or [bootstrap.hooks.before-tools], while mise parses config during bootstrap.

Common situations: Guessing phase names from other tools' hook systems (pre-commit, Husky) instead of checking the list; typos and singular/plural mistakes; configs written against docs that renamed phases.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/43e3fb3c059a1993. Report an issue: GitHub.