Skip to content

child_process: fix stdin close event when child closes fd 0#64626

Open
mhayk wants to merge 1 commit into
nodejs:mainfrom
mhayk:fix/child_process_stdin_close
Open

child_process: fix stdin close event when child closes fd 0#64626
mhayk wants to merge 1 commit into
nodejs:mainfrom
mhayk:fix/child_process_stdin_close

Conversation

@mhayk

@mhayk mhayk commented Jul 20, 2026

Copy link
Copy Markdown

Fixes #25131

Problem

When a child process closes its stdin file descriptor (fd 0), the parent process should receive a 'close' event on the stdin socket. This was working in v8.11.4 but broke in PR #18701.

Root Cause

PR #18701 explicitly set readable: true when creating child stdio sockets, which prevented net.Socket from calling read(0). Without read(0), libuv doesn't listen for EOF/HUP events from the child closing fd 0, so the 'close' event is never emitted.

Solution

For stdin specifically (index 0), create the socket without explicitly setting the readable option. This allows net.Socket to call read(0) in its constructor. After socket creation, manually set readable = false and writable = true to maintain correct semantic properties.

For all other stdio sockets (stdout, stderr, custom pipes), the original behavior is preserved.

Testing

  • Manual reproducer test passes ✅
  • New regression test test-child-process-stdin-close.js passes ✅
  • Existing test test-child-process-stdin.js passes ✅
  • All stdin-related tests pass ✅

Fixes nodejs#25131

When a child process closes its stdin file descriptor (fd 0), the parent
process should receive a 'close' event on the stdin socket. This was
working in v8.11.4 but broke in PR nodejs#18701.

The root cause: PR nodejs#18701 explicitly set `readable: true` when creating
child stdio sockets, which prevented net.Socket from calling `read(0)`.
Without `read(0)`, libuv doesn't listen for EOF/HUP events from the child
closing fd 0, so the 'close' event is never emitted.

Solution: For stdin specifically (index 0), create the socket without
explicitly setting the `readable` option. This allows net.Socket to call
`read(0)` in its constructor. After the socket is created, manually set
`readable = false` and `writable = true` to maintain the correct
semantic properties of stdin.

For all other stdio sockets (stdout, stderr, and custom pipes), the
original behavior is preserved.

Reviewed-By: Mhayk Whandson <hi@mhayk.com>
@nodejs-github-bot nodejs-github-bot added child_process Issues and PRs related to the child_process subsystem. needs-ci PRs that need a full CI run. labels Jul 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

child_process Issues and PRs related to the child_process subsystem. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

regression: child_process stdin pipe close event not emitted

2 participants