{"record":{"id":"b87ac38c13f3b230","repo":"Hmbown/CodeWhale","slug":"hook-helper-did-not-finish-within-its-timeout","errorCode":null,"errorMessage":"hook helper did not finish within its timeout","messagePattern":"hook helper did not finish within its timeout","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/hooks/executor.rs","lineNumber":1114,"sourceCode":"    if status.success() {\n        Ok(())\n    } else {\n        Err(std::io::Error::other(format!(\n            \"taskkill exited with {status}\"\n        )))\n    }\n}\n\n#[cfg(any(windows, test))]\nfn wait_for_helper_status(\n    child: &mut Child,\n    timeout: Duration,\n) -> std::io::Result<std::process::ExitStatus> {\n    match child.wait_timeout(timeout)? {\n        Some(status) => Ok(status),\n        None => {\n            let _ = kill_and_reap_immediate_child(child, HOOK_REAP_TIMEOUT);\n            Err(std::io::Error::new(\n                std::io::ErrorKind::TimedOut,\n                \"hook helper did not finish within its timeout\",\n            ))\n        }\n    }\n}\n\nfn kill_and_reap_immediate_child(child: &mut Child, timeout: Duration) -> bool {\n    let _ = child.kill();\n    matches!(child.wait_timeout(timeout), Ok(Some(_)))\n}\n\n/// Spawn a contained hook child.\n///\n/// Errors returned here are deliberately free of the hook command, the\n/// resolved interpreter path, and the OS message: the caller turns them into a\n/// user-visible \"hook could not answer\" receipt, and on Windows a raw spawn\n/// error echoes the whole command line back. The detail is logged instead.","sourceCodeStart":1096,"sourceCodeEnd":1132,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/hooks/executor.rs#L1096-L1132","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Increase the hook's configured timeout to cover realistic helper runtime","Fix the helper so it terminates: close stdin, remove interactive prompts, add its own internal timeouts","Remove or gate the slow hook if it cannot be made deterministic"],"exampleFix":"// before\n{\"command\": \"slow-linter --remote\", \"timeout_ms\": 1000}\n// after\n{\"command\": \"slow-linter --offline\", \"timeout_ms\": 10000}","handlingStrategy":"try-catch","validationCode":"// before spawning: ensure the helper is non-interactive and bounded\ne.g. assert the hook command runs with stdin closed and has its own deadline","typeGuard":null,"tryCatchPattern":"match wait_with_timeout(child, configured_timeout) {\n    Ok(status) if status.success() => proceed(),\n    Ok(status) => report_hook_failure(status),\n    Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {\n        eprintln!(\"hook exceeded its timeout; increase timeout_ms or fix the helper\");\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Size each hook's timeout to measured worst-case runtime","Ensure hook helpers never read stdin or prompt interactively","Give slow helpers (network linters, builds) internal timeouts and offline modes"],"tags":["timeout","hooks","process","subprocess"],"backgroundTag":"request-timeout","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}