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 = runsView on GitHub (pinned to 9dcfcaa0dc)
Solutions
- 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)
- Check for singular/plural mistakes (it is pre-packages, not pre-package)
- 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
- Copy phase names from the error message or docs, not from other tools' hook systems
- Run mise bootstrap right after adding hooks to catch typos early
- Prefer dash-form names (pre-packages); underscores work but dashes match the docs
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
- absent bootstrap group '{name}' must not set gid or system
- present bootstrap user '{name}' requires a primary group
- present bootstrap user '{name}' must not set remove_home
- absent bootstrap user '{name}' may only set state and remove
- expected `run` to be a string or array of strings
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/43e3fb3c059a1993.
Report an issue: GitHub.