From e22ed208f7794297e49bd0f3ade05d0a4b907126 Mon Sep 17 00:00:00 2001 From: jacob-wang Date: Fri, 3 Apr 2026 21:43:07 +0800 Subject: [PATCH] fix: handle EAGAIN when reading hook stdin in non-blocking mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When Claude Code invokes hooks with stdin in non-blocking mode, the kernel returns EAGAIN instead of blocking. Node's synchronous readFileSync does not retry on EAGAIN — it throws. Both hook scripts hit this intermittently on macOS when multiple hooks fire in quick succession. Fix: catch EAGAIN in readHookInput() and treat it as empty input in both stop-review-gate-hook.mjs and session-lifecycle-hook.mjs, matching the existing empty-string handling path. Closes #120 Co-Authored-By: Claude Sonnet 4.6 --- plugins/codex/scripts/session-lifecycle-hook.mjs | 10 +++++++++- plugins/codex/scripts/stop-review-gate-hook.mjs | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/plugins/codex/scripts/session-lifecycle-hook.mjs b/plugins/codex/scripts/session-lifecycle-hook.mjs index 9655eae..3d0dc75 100644 --- a/plugins/codex/scripts/session-lifecycle-hook.mjs +++ b/plugins/codex/scripts/session-lifecycle-hook.mjs @@ -20,7 +20,15 @@ export const SESSION_ID_ENV = "CODEX_COMPANION_SESSION_ID"; const PLUGIN_DATA_ENV = "CLAUDE_PLUGIN_DATA"; function readHookInput() { - const raw = fs.readFileSync(0, "utf8").trim(); + let raw; + try { + raw = fs.readFileSync(0, "utf8").trim(); + } catch (e) { + if (e.code === "EAGAIN") { + return {}; + } + throw e; + } if (!raw) { return {}; } diff --git a/plugins/codex/scripts/stop-review-gate-hook.mjs b/plugins/codex/scripts/stop-review-gate-hook.mjs index c22edbd..ccabc7f 100644 --- a/plugins/codex/scripts/stop-review-gate-hook.mjs +++ b/plugins/codex/scripts/stop-review-gate-hook.mjs @@ -19,7 +19,15 @@ const ROOT_DIR = path.resolve(SCRIPT_DIR, ".."); const STOP_REVIEW_TASK_MARKER = "Run a stop-gate review of the previous Claude turn."; function readHookInput() { - const raw = fs.readFileSync(0, "utf8").trim(); + let raw; + try { + raw = fs.readFileSync(0, "utf8").trim(); + } catch (e) { + if (e.code === "EAGAIN") { + return {}; + } + throw e; + } if (!raw) { return {}; }