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:trueBuffer the output from the spawned process. When buffering is disabled you must consume the output of the
stdoutandstderrstreams 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:
allboolean option defaulting tofalseWhat 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
stdoutorstderrcan make the child process not exitThe child process does not emit the
exitevent (i.e. theexeca()returned promise does not resolve) when the eitherstdoutorstderr:219214bytes on my machine)stdio: 'pipe'(the default value)Using
buffer: true(the default value) reads all streams, so this only happens whenbufferisfalse.2)
allmust be consumed for the child process to exit, whenstdoutorstderris bigWhen the conditions above are met,
allalso need to be read, for the same reasons. Howeverallis different fromstdoutandstderr:execanot by Node.js core. It’s aPassThroughthat gets bothstdoutandstderras piped input.stdoutorstderr3) Waiting for streams completion when
bufferisfalseis problematicWhen
bufferisfalseandstdout,stderrorallusesstdio: 'pipe'(the default value), we wait for the stream to complete.However:
endanderrorevents, but should also wait for thecloseandfinishevents.