Hmbown/CodeWhale · error · io::Error

hook helper did not finish within its timeout

Error message

hook helper did not finish within its timeout

What it means

This error means a hook helper subprocess did not exit within its allotted timeout. The library kills and reaps the child (kill_and_reap_immediate_child with HOOK_REAP_TIMEOUT) and then throws TimedOut, since a hook exceeding its deadline is treated as failed.

Solutions

  1. Increase the hook's configured timeout to cover realistic helper runtime
  2. Fix the helper so it terminates: close stdin, remove interactive prompts, add its own internal timeouts
  3. Remove or gate the slow hook if it cannot be made deterministic

Example fix

// before
{"command": "slow-linter --remote", "timeout_ms": 1000}
// after
{"command": "slow-linter --offline", "timeout_ms": 10000}
Defensive patterns

Strategy: try-catch

Validate before calling

// before spawning: ensure the helper is non-interactive and bounded
e.g. assert the hook command runs with stdin closed and has its own deadline

Try / catch

match wait_with_timeout(child, configured_timeout) {
    Ok(status) if status.success() => proceed(),
    Ok(status) => report_hook_failure(status),
    Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
        eprintln!("hook exceeded its timeout; increase timeout_ms or fix the helper");
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: A hook command spawned by the executor (wait_timeout with the configured hook timeout) still running when the deadline elapses — e.g. a network call, a long build, or an interactive prompt blocking the helper.

Common situations: A hook script waits on stdin (no tty) and hangs; a formatter/linter hits a slow network; the hook timeout is configured too aggressively for a legitimately slow helper.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/b87ac38c13f3b230. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/hooks/executor.rs:1114

    if status.success() {
        Ok(())
    } else {
        Err(std::io::Error::other(format!(
            "taskkill exited with {status}"
        )))
    }
}

#[cfg(any(windows, test))]
fn wait_for_helper_status(
    child: &mut Child,
    timeout: Duration,
) -> std::io::Result<std::process::ExitStatus> {
    match child.wait_timeout(timeout)? {
        Some(status) => Ok(status),
        None => {
            let _ = kill_and_reap_immediate_child(child, HOOK_REAP_TIMEOUT);
            Err(std::io::Error::new(
                std::io::ErrorKind::TimedOut,
                "hook helper did not finish within its timeout",
            ))
        }
    }
}

fn kill_and_reap_immediate_child(child: &mut Child, timeout: Duration) -> bool {
    let _ = child.kill();
    matches!(child.wait_timeout(timeout), Ok(Some(_)))
}

/// Spawn a contained hook child.
///
/// Errors returned here are deliberately free of the hook command, the
/// resolved interpreter path, and the OS message: the caller turns them into a
/// user-visible "hook could not answer" receipt, and on Windows a raw spawn
/// error echoes the whole command line back. The detail is logged instead.

View on GitHub (pinned to 73e0f67d83)