Skip to content

fix: handle EAGAIN in hook scripts readHookInput (#120) - #150

Open
D2758695161 wants to merge 3 commits into
openai:mainfrom
D2758695161:fix/handle-eagain-in-hook-read
Open

D2758695161 wants to merge 3 commits into
openai:mainfrom
D2758695161:fix/handle-eagain-in-hook-read

Conversation

@D2758695161

Copy link
Copy Markdown

Summary

Fixes #120: Hook scripts crash with EAGAIN: resource temporarily unavailable when reading non-blocking stdin.

Problem

readHookInput() uses fs.readFileSync(0, "utf8") to read stdin. When Claude Code invokes hooks with stdin as a non-blocking pipe, the kernel returns EAGAIN instead of blocking, causing Node.js to throw.

Solution

Wrap readFileSync in try/catch and treat EAGAIN 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;
  }
}

Files Changed

  • plugins/codex/scripts/stop-review-gate-hook.mjs
  • plugins/codex/scripts/session-lifecycle-hook.mjs

Fixes #120

— 一筒 🦀

Fixes openai#120 - Catch EAGAIN when reading non-blocking stdin in hooks
Fixes openai#120 - Catch EAGAIN when reading non-blocking stdin in hooks
@D2758695161
D2758695161 requested a review from a team April 5, 2026 02:58
@D2758695161

Copy link
Copy Markdown
Author

👋 Following up — ready for review!

— 一筒 🦀

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2d542f8521

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const { options, positionals } = parseCommandInput(argv, {
valueOptions: ["model", "effort", "cwd", "prompt-file"],
booleanOptions: ["json", "write", "resume-last", "resume", "fresh", "background"],
booleanOptions: ["json", "write", "full-access", "resume-last", "resume", "fresh", "background"],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Wire --full-access through task execution

handleTask now advertises --full-access, but the parsed value is never read or passed into the task request, so executeTaskRun always sees request.fullAccess as falsy and falls back to workspace-write/read-only. In practice, running task --full-access ... is a no-op for both foreground and background runs, which breaks the new CLI contract and can cause tasks requiring full sandbox access to fail unexpectedly.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2d542f8521

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const { options, positionals } = parseCommandInput(argv, {
valueOptions: ["model", "effort", "cwd", "prompt-file"],
booleanOptions: ["json", "write", "resume-last", "resume", "fresh", "background"],
booleanOptions: ["json", "write", "full-access", "resume-last", "resume", "fresh", "background"],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Wire --full-access through task execution

handleTask now advertises --full-access, but the parsed value is never read or passed into the task request, so executeTaskRun always sees request.fullAccess as falsy and falls back to workspace-write/read-only. In practice, running task --full-access ... is a no-op for both foreground and background runs, which breaks the new CLI contract and can cause tasks requiring full sandbox access to fail unexpectedly.

Useful? React with 👍 / 👎.

@D2758695161

Copy link
Copy Markdown
Author

👋 Checking in — still ready for review. Happy to address any feedback!

— 一筒 🦀

CBEPX added a commit to CBEPX/codex-plugin-cc that referenced this pull request Sep 28, 2026
…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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant