Repository navigation
Conversation
Claude Code invokes hooks with stdin as a non-blocking pipe on some
platforms (macOS, Linux). This causes fs.readFileSync(0) to throw
EAGAIN: resource temporarily unavailable, making the hook exit with
code 1 and showing a SessionStart:startup hook error banner on every
session start.
Fix by retrying readFileSync up to 20 times (200ms total) when EAGAIN
is received, using Atomics.wait for precise synchronous sleeping. If
all retries are exhausted, returns {} so the hook succeeds gracefully
rather than surfacing a startup error.
Fixes openai#120
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7927327b87
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…l reads When stdin arrives in multiple writes on a non-blocking pipe, readFileSync can consume a partial chunk internally before throwing EAGAIN, causing the already-read bytes to be silently discarded. On the next retry, readFileSync starts from an empty buffer, so the accumulated bytes are gone and JSON.parse fails on the tail chunk alone. Switch to readSync in a manual accumulation loop: each successful read appends to a chunks array, EAGAIN causes a wait-and-retry without touching the accumulated data, and EOF triggers a single Buffer.concat + JSON.parse. Fixes the review comment on openai#165.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c2d931d06d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
When the retry budget is exhausted after partial bytes have already been
read, breaking out of the loop and falling through to JSON.parse risks
throwing SyntaxError on truncated JSON, which fails the hook instead of
degrading gracefully. Return {} directly on max retries to guarantee
clean hook exit regardless of how much data was buffered.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b51bc28408
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…penai#255, openai#165, openai#253, openai#209, openai#239, openai#307, openai#315) - Raise adversarial review inline diff limits to 50 files / 1MB (openai#327) - Add wall-clock timeouts to JSON-RPC requests (openai#302) - Use codex-namespaced clientInfo.name (openai#200, openai#255) - Handle EAGAIN in hook scripts for non-blocking stdin (openai#165) - Replace Node.js SessionStart hook with faster bash script (openai#253) - Add --fast flag for service_tier support (openai#209) - Set timeout: 600000 on foreground review/adversarial-review flows (openai#239) - Rename gpt-5-4-prompting skill to codex-prompting (openai#307) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…eview prompt via stdin New lib/hook-input.mjs readHookInput(): streams stdin with a deadline (2 s default, CODEX_HOOK_STDIN_TIMEOUT_MS for tests), a 1 MiB byte limit and a StringDecoder across chunk boundaries. EOF parses the whole input; at the deadline a buffer that is already a complete JSON object is accepted (only the EOF was late), anything else is `timeout`; over the limit is `overflow` without parsing; bad JSON is `invalid-json`. Listeners and the timer are removed and stdin destroyed, so the hook exits right after deciding. Stop hook: any read error blocks as before (fail-closed), except a timeout with nothing received while the workspace gate (CLAUDE_PROJECT_DIR / cwd) is off, which allows (openai#530/openai#544). With the gate on, a timeout blocks naming "hook input did not arrive". The review prompt now reaches the companion via `task --prompt-stdin`, not argv, lifting the Windows/Linux argv limits. SessionStart/SessionEnd hook: reads with a 1 s deadline before the 12 s SessionEnd budget starts; on any read error it prints one stderr line and returns without cleanup. The hooks.json timeout test now asserts 15 s > budget + read. readStdinIfPiped (companion): readSync loop that keeps accumulated bytes across EAGAIN on a non-blocking stdin (up to 50 x 20 ms consecutive) and throws on exhaustion instead of returning a partial prompt (openai#120/openai#247/openai#123/openai#150/openai#165). Co-authored-by: stantheman0128 <[email protected]> Co-authored-by: tmchow <[email protected]> Co-Authored-By: Claude Fable 5.1 <[email protected]>
Problem
Claude Code invokes hooks with stdin as a non-blocking pipe on macOS and Linux. This causes
fs.readFileSync(0)inreadHookInput()to throwEAGAIN: resource temporarily unavailable, making the hook exit with code 1. The result is aSessionStart:startup hook errorbanner shown on every session start.Reported in #120.
Root cause
Fix
Retry
readFileSyncup to 20 times (200ms total) whenEAGAINis received, usingAtomics.waitfor synchronous sleeping. If all retries are exhausted, returns{}so the hook exits cleanly (graceful degradation) instead of surfacing a startup error.Applied to both
session-lifecycle-hook.mjsandstop-review-gate-hook.mjs.Relation to PR #123
PR #123 addresses the same issue with a more complex buffered approach. This PR offers a simpler implementation with the same intent.
Fixes #120