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

  1. Run the hook command manually in the project root to see its actual output/error and fix the script
  2. Check that the command exists on PATH at the time hooks run (mise shims vs system PATH)
  3. Fix the hook script so it exits 0 on success paths; guard optional steps so they don't fail the install
  4. 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

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


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/578b1253a1233c7f. Report an issue: GitHub.