Description
Both stop-review-gate-hook.mjs and session-lifecycle-hook.mjs crash intermittently with:
Error: EAGAIN: resource temporarily unavailable, read
at Object.readFileSync (node:fs:449:20)
at readHookInput (file:///.claude/plugins/marketplaces/openai-codex/plugins/codex/scripts/stop-review-gate-hook.mjs:22:18)
Root Cause
readHookInput() uses fs.readFileSync(0, "utf8") to read stdin. When Claude Code invokes hooks with stdin as a pipe in non-blocking mode, the kernel returns EAGAIN instead of blocking. Node.js synchronous APIs don't retry — they throw.
// Both scripts, line 22-23
function readHookInput() {
const raw = fs.readFileSync(0, "utf8").trim(); // throws EAGAIN
if (!raw) { return {}; }
return JSON.parse(raw);
}
Reproduction
Happens intermittently across Claude Code sessions on macOS (Darwin 25.3.0, Node.js v20.19.6). More frequent when multiple hooks fire in quick succession.
Suggested Fix
Catch EAGAIN and treat as empty input:
function readHookInput() {
try {
const raw = fs.readFileSync(0, "utf8").trim();
if (!raw) return {};
return JSON.parse(raw);
} catch (e) {
if (e.code === 'EAGAIN') return {};
throw e;
}
}
Both stop-review-gate-hook.mjs and session-lifecycle-hook.mjs need the same fix.
Environment
- macOS Darwin 25.3.0
- Node.js v20.19.6
- Plugin version: 1.0.2 (commit 8e403f9)
- Claude Code v2.1.90
Description
Both
stop-review-gate-hook.mjsandsession-lifecycle-hook.mjscrash intermittently with:Root Cause
readHookInput()usesfs.readFileSync(0, "utf8")to read stdin. When Claude Code invokes hooks with stdin as a pipe in non-blocking mode, the kernel returnsEAGAINinstead of blocking. Node.js synchronous APIs don't retry — they throw.Reproduction
Happens intermittently across Claude Code sessions on macOS (Darwin 25.3.0, Node.js v20.19.6). More frequent when multiple hooks fire in quick succession.
Suggested Fix
Catch
EAGAINand treat as empty input:Both
stop-review-gate-hook.mjsandsession-lifecycle-hook.mjsneed the same fix.Environment