Repository navigation
EPIPE when writing to newly created child process's stdin #40085
Description
Activity
- addedchild_processIssues and PRs related to the child_process subsystem.Issues and PRs related to the child_process subsystem.
on Sep 11, 2021 It should be noted that this code is already resulting in this error on
v16.6.0and evenv16.2.0, at least on Alpine Linux. The same goes forv14.17.6from the Alpine repositories, as well as the same 14.x version from the Nodesource repositories on Ubuntu.Reacted by ehmickyIn fact, this same behavior shows on
v12.13.0as well.Is this really bug? The
echoprocess is actually gone when you actually try to write into itsstdin, thus theEPIPEerror.Reacted by ehmickyThanks @blattersturm and @santigimeno, I believe you are both correct.
I tried reproducing my original problem and it now reproduces with much older Node.js versions as mentioned by @blattersturm.
This seems to be a timing issue as pointed out by @santigimeno. For example, I have found that the following command works approximately half of the times on my machine (to reproduce, you need to adjust the size):
spawn('bash', ['-c', 'echo ' + 'a'.repeat(57367)], { stdio: ['pipe', 'ignore', 'inherit'] })
However, even if the child process is in fact gone (which is most likely to be the case), the
child_processinstance does not indicate it. Also, thestdinattributes and events indicate it as writable. For example:import { spawn } from 'child_process' const childProcess = spawn('echo') console.log('childProcess pid', childProcess.pid) childProcess.on('spawn', () => { console.log('childProcess spawn') }) childProcess.on('exit', (exitCode, signal) => { console.log('childProcess exit', exitCode, signal) }) childProcess.on('close', (exitCode, signal) => { console.log('childProcess close', exitCode, signal) }) childProcess.on('error', (error) => { console.log('childProcess error', error) }) childProcess.stdin.on('error', (error) => { console.log('stdin error', error) }) console.log('stdin writable', childProcess.stdin.writable) console.log('stdin writableEnded', childProcess.stdin.writableEnded) console.log('stdin writableFinished', childProcess.stdin.writableFinished) console.log('stdin destroyed', childProcess.stdin.destroyed) console.log('before stdin write') childProcess.stdin.write('test')
Results in:
childProcess pid 35573 stdin writable true stdin writableEnded false stdin writableFinished false stdin destroyed false before stdin write childProcess spawn stdin error Error: write EPIPE at afterWriteDispatched (node:internal/stream_base_commons:164:15) at writeGeneric (node:internal/stream_base_commons:155:3) at Socket._writeGeneric (node:net:780:11) at Socket._write (node:net:792:8) at writeOrBuffer (node:internal/streams/writable:389:12) at _write (node:internal/streams/writable:330:10) at Socket.Writable.write (node:internal/streams/writable:334:10) at file:///home/ether/Desktop/a.js:29:20 at ModuleJob.run (node:internal/modules/esm/module_job:183:25) at async Loader.import (node:internal/modules/esm/loader:178:24) { errno: -32, code: 'EPIPE', syscall: 'write' } childProcess exit 0 null childProcess close 0 nullAre those attributes and events correct considering
stdin.write()fails?As a consequence, it becomes impossible to do the following safely: write to a child process's
stdinas it starts. This is very useful and used byexeca'sinputfeature, for example.The best ways I can think of to solve this would be:
- To ignore
EPIPEerrors onstdinuntil child process has emitted thespawnevent. However, this is bad as it might ignore actual unrelated errors. - To create a stream from the string/buffer input and pass this to the
stdiooption ofchild_process.spawn().
Is there a better way to do this?
- To ignore
- changed the title
[-]`EPIPE` when writing to child process's stdin since Node `16.7.0`[/-][+]`EPIPE` when writing to child process's stdin[/+]on Sep 23, 2021 - changed the title
[-]`EPIPE` when writing to child process's stdin[/-][+]`EPIPE` when writing to newly created child process's stdin[/+]on Sep 23, 2021 Facing the same issue on
v16.18.0readable.pipe(child.stdin, { end: true }) // pipeline API also breaksError: write EPIPE at WriteWrap.onWriteComplete [as oncomplete] (node:internal/stream_base_commons:94:16) { errno: -32, code: 'EPIPE', syscall: 'write' }I'll go ahead and close this because I believe the consensus is that this isn't a bug (and I agree.) The child process object and the stdio streams emit various events that you can observe to know when it's safe to read and write.
@bnoordhuis There is no flag or event which may be observed to write to stdin safely, see discussion at #48786
TL;DR see the comment #48786 (comment)
But we don't have any way to do that when we use the
clustermodule... @bnoordhuis
Version
16.7.0 (changelog)
Platform
Linux ether-laptop 5.11.0-34-generic #36-Ubuntu SMP Thu Aug 26 19:22:09 UTC 2021 x86_64 x86_64 x86_64 GNU/LinuxSubsystem
child_process
What steps will reproduce the bug?
How often does it reproduce? Is there a required condition?
With Node
<=16.6.0, this creates no error.Also, this only produces an error with specific binaries.
echodoes produce the error, butcatornode --versiondo not.Finally, this seems to happen on macOS and Linux, but not on Windows.
What is the expected behavior?
No error should be produced.
Also,
testshould be written to the child process'sstdin. Some modules like Execa rely on being able to pass a string or buffer to a child process's stdin (upstream issue for Execa: sindresorhus/execa#474).What do you see instead?
With Node
>=16.7.0, this produces: