execa: Unfinished execa promise when path is not found and buffer=false
When running:
const proc = await execa("echo hello", [], {
buffer: false
});
This promise never resolves or rejects.
I investigated the source and found the problem on: https://github.com/sindresorhus/execa/blob/071a8154f882d13116a6a91d8691ea150de31753/lib/stream.js#L60-L67
And fixed using:
if (!buffer) {
// TODO: Use `ret = util.promisify(stream.finished)(stream);` when targeting Node.js 10
return new Promise((resolve, reject) => {
stream
.once('end', resolve)
.once('error', reject)
.once('close', resolve)
});
}
Adding .once('close', resolve)
solved the problem, since when the file is not found, it seems child_process.spawn
just closes the streams but not writes an end (?).
The solution proposed by the TODO comment works too, but I had a doubt about the targeting Node.js 10
part: The project is dropping compatibility with previous versions?
Another problem about the buffer option that I noticed is in the documentation:
buffer
Type:
boolean
Default:true
Buffer the output from the spawned process. When buffering is disabled you must consume the output of the
stdout
andstderr
streams because the promise will not be resolved/rejected until they have completed.
Consuming the stdout
and stderr
is not enough, the all
stream has to be consumed too, or just resumed. (consuming only the all
stream works too)
I would be glad to open a PR to fix these issues
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 1
- Comments: 29 (14 by maintainers)
The tests worked on my machine. Did some tests for the bugs we found and everything seemed ok
I’ve done some experiments and the following seems to work:
all
boolean option defaulting tofalse
What do you think?
Some tests. Does this behavior makes sense?
Ok so this is a summary of this issue. Please let me know if I got it correctly.
1) Big
stdout
orstderr
can make the child process not exitThe child process does not emit the
exit
event (i.e. theexeca()
returned promise does not resolve) when the eitherstdout
orstderr
:219214
bytes on my machine)stdio: 'pipe'
(the default value)Using
buffer: true
(the default value) reads all streams, so this only happens whenbuffer
isfalse
.2)
all
must be consumed for the child process to exit, whenstdout
orstderr
is bigWhen the conditions above are met,
all
also need to be read, for the same reasons. Howeverall
is different fromstdout
andstderr
:execa
not by Node.js core. It’s aPassThrough
that gets bothstdout
andstderr
as piped input.stdout
orstderr
3) Waiting for streams completion when
buffer
isfalse
is problematicWhen
buffer
isfalse
andstdout
,stderr
orall
usesstdio: 'pipe'
(the default value), we wait for the stream to complete.However:
end
anderror
events, but should also wait for theclose
andfinish
events.