jdx/mise · error
hook command failed: {status}
Error message
hook command failed: {status} What it means
mise hooks (pre/post install, activate, etc.) execute a user-defined command and require a zero exit status. When the hook's child process exits non-zero, execute wraps the exit status in this error, propagating the hook failure to the caller (run_matched_hook).
Source
Thrown at src/hooks.rs:775
let mut c = std::process::Command::new(&shell[0]);
for a in &cmd_args {
c.raw_arg(a);
}
c.env_clear();
c.envs(env.iter());
if matches!(hook.hook, Hooks::Preinstall | Hooks::Postinstall) {
c.current_dir(project_root);
}
// Send the hook's stdout to mise's stderr (matching the duct
// `stdout_to_stderr()` the non-cmd path uses) by handing the child a
// clone of our stderr handle. Redirecting the descriptor directly —
// rather than piping through a reader thread — means a hook that
// spawns a background child holding the write end can't block us
// waiting for pipe EOF, and stdout/stderr ordering is preserved.
c.stdout(std::io::stderr().as_handle().try_clone_to_owned()?);
let status = c.status()?;
if !status.success() {
eyre::bail!("hook command failed: {status}");
}
return Ok(());
}
}
let cwd = matches!(hook.hook, Hooks::Preinstall | Hooks::Postinstall).then_some(project_root);
let mut command = cmd(&shell[0], args).full_env(&env);
if let Some(cwd) = cwd {
command = command.dir(cwd);
}
crate::inline_command::optimize_expression(command, run, &env, cwd, direct_enabled)
.stdout_to_stderr()
.run()?;
Ok(())
}
async fn execute_task(
config: &Arc<Config>,View on GitHub (pinned to afd2eddd3a)
Solutions
- Run the hook command manually in the project root to see its actual output/error and fix the script
- Check that the command exists on PATH at the time hooks run (mise shims vs system PATH)
- Fix the hook script so it exits 0 on success paths; guard optional steps so they don't fail the install
- Temporarily comment out the hook in mise.toml to unblock the main operation, fix, then re-enable
Example fix
// before (mise.toml) [hooks] postinstall = "./scripts/setup.sh --strict" # exits 1 when optional deps missing // after [hooks] postinstall = "./scripts/setup.sh || echo 'setup skipped'"
Defensive patterns
Strategy: try-catch
Validate before calling
// verify the hook command exists before running
let status = std::process::Command::new("sh").arg("-c").arg(&hook_cmd)
.status()?;
assert!(status.success(), "hook would fail: {hook_cmd}"); Try / catch
if let Err(e) = run_matched_hook(&hook, ...) {
eprintln!("hook {} failed: {e:#}; continuing or aborting per policy", hook.name());
// decide: abort the install, or log and continue
} Prevention
- Test hook scripts manually in a clean shell mimicking mise's PATH
- Make hooks idempotent and tolerant of missing optional tools
- Keep hooks minimal; move complex logic into robust project scripts
When it happens
Trigger: A hook configured in mise.toml ([hooks] preinstall/postinstall or a plugin hook) runs a command that exits non-zero; the command is missing, crashes, or its script returns failure.
Common situations: postinstall script referencing a tool not yet installed (PATH issues); hook script with a syntax error or missing executable bit; hook relies on env vars not present at hook time; project scripts failing during a shared `mise install`.
Related errors
- exited with {status}
- {} {err}
- timed out after {duration:?}
- command output exceeded {max_output_bytes} bytes
- failed to read command {stream}: {err}
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/578b1253a1233c7f.
Report an issue: GitHub.