Throwing an error in a command handler can result in truncated stdout/stderr output
See original GitHub issueThanks for such a fantastic library. I wanted to help out by dropping a report for a rare bug I saw in case it helps anyone else.
Problem
I was debugging a problem with webpack output getting truncated on failures. This build is wrapped in a yargs
command handler. The code looks roughly like this:
async function commandHandler() {
const compiler = webpack(config);
const stats = await util.promisify(compiler.run.bind(compiler))();
if (stats.hasErrors()) {
console.log(stats.toString());
throw new Error("Webpack build failed. See error messages above.")
}
}
This example is adapted slightly from: https://webpack.js.org/api/node/#error-handling
The stats.toString()
object here could be quite large, and I was observing output from webpack getting cut off mid-line. It was clear the stdout output wasn’t complete.
Investigation
Making sure stdout
was flushed before throwing an error fixed the issue.
console.log(stats.toString());
await new Promise((resolve) => process.stdout.write("", resolve));
throw new Error("Webpack build failed. See error messages above.")
This made me think there was a code path in yargs
that called process.exit()
after a command handler returns, which would force Node.js to exit before everything on the event queue is processed. Node.js normally waits for all stdout
to be written before exiting.
I was able to repro yargs
calling process.exit
in a debugger. The code path is:
-
yargs
callsyargs.getInternalMethods().getUsageInstance().fail
if a command handler returns a rejecting promise: -
This
fail
handler callsyargs.exit(1)
ifexitProcess
is set. (It is by default.)https://github.com/yargs/yargs/blob/59a86fb83cfeb8533c6dd446c73cf4166cc455f2/lib/usage.ts#L70-L71
-
yargs.exit
then callsprocess.exit
Workarounds
- I ended up setting
process.exitCode = 1
instead of throwing an error. - This is also fixable by adding
exitProcess(false)
, but theyargs
docs discourage setting that option for commands.
Issue Analytics
- State:
- Created 2 years ago
- Comments:9 (4 by maintainers)
I believe the reasoning was if you’ve triggered an event that should terminate execution, like
--help
, you don’t want the logic to execute after yargs is invoked.I think we should leave it defaulting to
true
.Can you confirm that the truncation stops if you remove the check for
stream.isTTY
, if it does, I would happily take a PR for this fix.Thanks @bcoe. Removing the condition might make sense. Would it help if I put up a PR for that?
The other alternative would be to switch the
exitProcess(...)
default fromtrue
tofalse
. This would avoid callingprocess.exit
, which @zaripych also cautioned against. I saw the caveat about that duplicating promise rejection errors though.true
?