🐛 Handle EPIPE on stdin in POSIX process implementation#198
🐛 Handle EPIPE on stdin in POSIX process implementation#198
Conversation
The POSIX createPosixProcess had no error handler on childProcess.stdin. When a child exits before a parent stdin write completes, Node emits EPIPE which surfaced as an uncaught exception. The Windows implementation already handled this. This adds the same suppression for POSIX and routes non-EPIPE stdin errors through the existing processResult error path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughUpdated package version and added deterministic stdin "error" handlers on POSIX and Windows paths that suppress EPIPE (logging a warning) and surface other stdin errors by resolving the process result. Added a test and fixture that verify writing to stdin after child exit is handled without crashing. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error, 1 warning)
✅ Passed checks (2 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
commit: |
Route non-EPIPE stdin errors through processResult instead of throwing from the event callback, matching the pattern applied in posix.ts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-locate bind/unbind for the stdin error handler using Effection's action() primitive. The cleanup function runs automatically when the spawned task is halted during scope teardown. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@process/src/exec/stdin.ts`:
- Line 5: Duplicate tuple alias ProcessResultValue is defined in multiple exec
modules; extract it into a single shared module (e.g., create an api module) and
export it for reuse. Create/process/src/exec/api.ts with an exported type
ProcessResultValue = [number?, string?]; then remove the local alias from
stdin.ts, posix.ts, and win32.ts and replace with an import of
ProcessResultValue from that shared api module (or re-export from an index if
preferred) so all exec implementations reference the same type definition.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 0cafae46-d53b-409c-b673-a29e8fb4b93d
📒 Files selected for processing (3)
process/src/exec/posix.tsprocess/src/exec/stdin.tsprocess/src/exec/win32.ts
The action() approach removed the EPIPE handler too early — before the finally block writes to stdin during Windows graceful shutdown. Use named handler with explicit .off() at the end of finally instead, ensuring the handler survives through all cleanup writes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
What if we also show a warning in dev mode? |
Logs a warning when writing to stdin of an already-exited child process, helping developers spot logic bugs where writes aren't gated on join(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Mentioned in discord but for posterity: Is there a case where we wouldn't want to no-op outside of shutdown? I suspect it is pretty safe to ignore in shutdown. I can't think of a truly useful situation, but gut says that it might be worth somehow allowing the user to deal with the errors outside of shutdown. A possible example may be that a process that starts, looks for user input, then runs "read only" to completion? |
|
Why are we trying to write to stdin after it has been closed? In other words, shouldn't we be detecting stdin close when it happens? |
Summary
childProcess.stdinerror handler in POSIXcreatePosixProcessto suppressEPIPEerrors when writing to stdin after child exitprocessResult.resolve(Err(err))— the same error propagation path used bytrapError@effectionx/processto0.7.4Root cause
The Windows implementation (
win32.ts:130-137) already suppressed EPIPE onchildProcess.stdin, but the POSIX implementation had no error handler at all. Any EPIPE from a write-after-exit became an uncaught exception.Behavioral contract after fix
join()/expect()stdin.send()remains fire-and-forget (voidreturn)Test plan
stdin EPIPE handling > does not crash when writing to stdin after child exitsread-one-line.js— reads one line from stdin, confirms via stdout, exitsstdin.send()callSummary by CodeRabbit
Chores
Bug Fixes
Tests