Skip to content

Different behavior of exit listener of spawned process #25137

Closed
@RoobinGood

Description

@RoobinGood

Process started by child_process module can start its own child process. For example tar can start child process if we use it in concrete directory. And if we choose unexisting directory for archive destination it can fail in different way:

$ tar -C /some/directory -cvzf /path/to/archive.tgz fileToArchive
tar (child): /path/to/archive.tgz: Cannot open: No such file or directory
tar (child): Error is not recoverable: exiting now
example.js
$ echo $?
141

# and sometimes (1 from 10 or more runs) it can receive exit code 2 and finish correctly
$ tar -C /some/directory -cvzf /path/to/archive.tgz fileToArchive
fileToArchive
tar (child): /path/to/archive.tgz: Cannot open: No such file or directory
tar (child): Error is not recoverable: exiting now
tar: Child returned status 2
tar: Error is not recoverable: exiting now
$ echo $?
2

The problem here is that on NodeJS <8.2.0 it's always exit with code 2 because child process emit this exit code.
For example this script example.js:

const spawn = require('child_process').spawn;
const Steppy = require('twostep').Steppy;
const pathUtils = require('path');
const assert = require('assert');

const exec = function(cmd, args) {
	return new Promise((resolve, reject) => {
		cmdSpawn = spawn(
			cmd,
			args,
			{stdio: ['pipe', 'pipe', 'pipe']}
		);

		let stdoutData = '';
		cmdSpawn.stdout.on('data', (data) => {
			stdoutData += data;
		});

		let stderrData = '';
		cmdSpawn.stderr.on('data', (data) => {
			stderrData += data;
		});

		cmdSpawn.on('exit', (exitCode, signal) => {
			console.log(exitCode, signal)
			resolve({
				stdoutData,
				stderrData,
				exitCode
			});
		});

		cmdSpawn.on('error', (err) => {
			promise.reject(new Error(err));
		});
	});
};


exec('tar', [
	'-C',
	__dirname,
	'-cvzf',
	pathUtils.join(__dirname, '/unexisted/path/archive.tgz'),
	'example.js'
])
	.then((result) => {
		console.log(result);

		assert.equal(result.exitCode, 2);
	})
	.catch((err) => {
		console.error(err.stack || err);
	});

will receive exit code 2 always on NodeJS 8.1.4 and this result on 8.2.0:

$ node example.js 
null 'SIGPIPE'
{ stdoutData: 'example.js\n',
  stderrData: 'tar (child): /home/robingood/work/gitlab/parking/price-calculator/scripts/unexisted/path/archive.tgz: Cannot open: No such file or directory\ntar (child): Error is not recoverable: exiting now\n',
  exitCode: null }
AssertionError [ERR_ASSERTION]: null == 2
    at exec.then (/home/robingood/work/gitlab/parking/price-calculator/scripts/example.js:50:10)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:189:7)

Why it happens? Is it correct behavior? What is right workaround for this case: handle signal in exit listener?

Metadata

Metadata

Assignees

No one assigned

    Labels

    child_processIssues and PRs related to the child_process subsystem.questionIssues that look for answers.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions