Skip to content

EAGAIN crash in hook scripts: readFileSync(0) fails when stdin is non-blocking #120

@Anfed-2030

Description

@Anfed-2030

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions