Skip to content

Commit b1e6c0d

Browse files
committed
errors, child_process: use internal/errors codes
PR-URL: #14998 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Stephen Belanger <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Anatoli Papirovski <[email protected]>
1 parent 59e4832 commit b1e6c0d

9 files changed

+209
-159
lines changed

doc/api/errors.md

+11
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,17 @@ Node.js was unable to watch for the `SIGINT` signal.
642642

643643
A child process was closed before the parent received a reply.
644644

645+
<a id="ERR_CHILD_PROCESS_IPC_REQUIRED"></a>
646+
### ERR_CHILD_PROCESS_IPC_REQUIRED
647+
648+
Used when a child process is being forked without specifying an IPC channel.
649+
650+
<a id="ERR_CHILD_PROCESS_STDIO_MAXBUFFER"></a>
651+
### ERR_CHILD_PROCESS_STDIO_MAXBUFFER
652+
653+
Used when the main process is trying to read data from the child process's
654+
STDERR / STDOUT, and the data's length is longer than the `maxBuffer` option.
655+
645656
<a id="ERR_CONSOLE_WRITABLE_STREAM"></a>
646657
### ERR_CONSOLE_WRITABLE_STREAM
647658

lib/child_process.js

+62-23
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const { createPromise,
2929
const debug = util.debuglog('child_process');
3030
const { Buffer } = require('buffer');
3131
const { Pipe, constants: PipeConstants } = process.binding('pipe_wrap');
32+
const errors = require('internal/errors');
3233
const { errname } = process.binding('uv');
3334
const child_process = require('internal/child_process');
3435
const {
@@ -46,7 +47,7 @@ function stdioStringToArray(option) {
4647
case 'inherit':
4748
return [option, option, option, 'ipc'];
4849
default:
49-
throw new TypeError('Incorrect value of stdio option: ' + option);
50+
throw new errors.TypeError('ERR_INVALID_OPT_VALUE', 'stdio', option);
5051
}
5152
}
5253

@@ -63,7 +64,9 @@ exports.fork = function(modulePath /*, args, options*/) {
6364

6465
if (pos < arguments.length && arguments[pos] != null) {
6566
if (typeof arguments[pos] !== 'object') {
66-
throw new TypeError('Incorrect value of args option');
67+
throw new errors.TypeError('ERR_INVALID_ARG_VALUE',
68+
`arguments[${pos}]`,
69+
arguments[pos]);
6770
}
6871

6972
options = util._extend({}, arguments[pos++]);
@@ -91,7 +94,8 @@ exports.fork = function(modulePath /*, args, options*/) {
9194
options.stdio = options.silent ? stdioStringToArray('pipe') :
9295
stdioStringToArray('inherit');
9396
} else if (options.stdio.indexOf('ipc') === -1) {
94-
throw new TypeError('Forked processes must have an IPC channel');
97+
throw new errors.Error('ERR_CHILD_PROCESS_IPC_REQUIRED',
98+
'options.stdio');
9599
}
96100

97101
options.execPath = options.execPath || process.execPath;
@@ -195,7 +199,7 @@ exports.execFile = function(file /*, args, options, callback*/) {
195199
}
196200

197201
if (!callback && pos < arguments.length && arguments[pos] != null) {
198-
throw new TypeError('Incorrect value of args option');
202+
throw new errors.TypeError('ERR_INVALID_ARG_VALUE', 'args', arguments[pos]);
199203
}
200204

201205
// Validate the timeout, if present.
@@ -322,7 +326,8 @@ exports.execFile = function(file /*, args, options, callback*/) {
322326
stdoutLen += encoding ? Buffer.byteLength(chunk, encoding) : chunk.length;
323327

324328
if (stdoutLen > options.maxBuffer) {
325-
ex = new Error('stdout maxBuffer exceeded');
329+
ex = new errors.RangeError('ERR_CHILD_PROCESS_STDIO_MAXBUFFER',
330+
'stdout');
326331
kill();
327332
} else if (encoding) {
328333
_stdout += chunk;
@@ -340,7 +345,8 @@ exports.execFile = function(file /*, args, options, callback*/) {
340345
stderrLen += encoding ? Buffer.byteLength(chunk, encoding) : chunk.length;
341346

342347
if (stderrLen > options.maxBuffer) {
343-
ex = new Error('stderr maxBuffer exceeded');
348+
ex = new errors.RangeError('ERR_CHILD_PROCESS_STDIO_MAXBUFFER',
349+
'stderr');
344350
kill();
345351
} else if (encoding) {
346352
_stderr += chunk;
@@ -377,13 +383,13 @@ function _convertCustomFds(options) {
377383

378384
function normalizeSpawnArguments(file, args, options) {
379385
if (typeof file !== 'string' || file.length === 0)
380-
throw new TypeError('"file" argument must be a non-empty string');
386+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'file', 'string', file);
381387

382388
if (Array.isArray(args)) {
383389
args = args.slice(0);
384390
} else if (args !== undefined &&
385391
(args === null || typeof args !== 'object')) {
386-
throw new TypeError('Incorrect value of args option');
392+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'args', 'object', args);
387393
} else {
388394
options = args;
389395
args = [];
@@ -392,41 +398,62 @@ function normalizeSpawnArguments(file, args, options) {
392398
if (options === undefined)
393399
options = {};
394400
else if (options === null || typeof options !== 'object')
395-
throw new TypeError('"options" argument must be an object');
401+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
402+
'options',
403+
'object',
404+
options);
396405

397406
// Validate the cwd, if present.
398407
if (options.cwd != null &&
399408
typeof options.cwd !== 'string') {
400-
throw new TypeError('"cwd" must be a string');
409+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
410+
'options.cwd',
411+
'string',
412+
options.cwd);
401413
}
402414

403415
// Validate detached, if present.
404416
if (options.detached != null &&
405417
typeof options.detached !== 'boolean') {
406-
throw new TypeError('"detached" must be a boolean');
418+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
419+
'options.detached',
420+
'boolean',
421+
options.detached);
407422
}
408423

409424
// Validate the uid, if present.
410425
if (options.uid != null && !Number.isInteger(options.uid)) {
411-
throw new TypeError('"uid" must be an integer');
426+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
427+
'options.uid',
428+
'integer',
429+
options.uid);
412430
}
413431

414432
// Validate the gid, if present.
415433
if (options.gid != null && !Number.isInteger(options.gid)) {
416-
throw new TypeError('"gid" must be an integer');
434+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
435+
'options.gid',
436+
'integer',
437+
options.gid);
417438
}
418439

419440
// Validate the shell, if present.
420441
if (options.shell != null &&
421442
typeof options.shell !== 'boolean' &&
422443
typeof options.shell !== 'string') {
423-
throw new TypeError('"shell" must be a boolean or string');
444+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
445+
'options.shell',
446+
['boolean', 'string'],
447+
options.shell);
424448
}
425449

426450
// Validate argv0, if present.
427451
if (options.argv0 != null &&
428452
typeof options.argv0 !== 'string') {
429-
throw new TypeError('"argv0" must be a string');
453+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
454+
'options.argv0',
455+
'string',
456+
options.argv0);
430457
}
431458

432459
// Validate windowsHide, if present.
@@ -438,7 +465,10 @@ function normalizeSpawnArguments(file, args, options) {
438465
// Validate windowsVerbatimArguments, if present.
439466
if (options.windowsVerbatimArguments != null &&
440467
typeof options.windowsVerbatimArguments !== 'boolean') {
441-
throw new TypeError('"windowsVerbatimArguments" must be a boolean');
468+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
469+
'options.windowsVerbatimArguments',
470+
'boolean',
471+
options.windowsVerbatimArguments);
442472
}
443473

444474
// Make a shallow copy so we don't clobber the user's options object.
@@ -549,10 +579,10 @@ function spawnSync(/*file, args, options*/) {
549579
} else if (typeof input === 'string') {
550580
pipe.input = Buffer.from(input, options.encoding);
551581
} else {
552-
throw new TypeError(util.format(
553-
'stdio[%d] should be Buffer, Uint8Array or string not %s',
554-
i,
555-
typeof input));
582+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
583+
('options.stdio[' + i + ']'),
584+
['Buffer', 'Uint8Array', 'string'],
585+
input);
556586
}
557587
}
558588
}
@@ -620,14 +650,20 @@ exports.execSync = execSync;
620650

621651
function validateTimeout(timeout) {
622652
if (timeout != null && !(Number.isInteger(timeout) && timeout >= 0)) {
623-
throw new TypeError('"timeout" must be an unsigned integer');
653+
throw new errors.RangeError('ERR_VALUE_OUT_OF_RANGE',
654+
'timeout',
655+
'an unsigned integer',
656+
timeout);
624657
}
625658
}
626659

627660

628661
function validateMaxBuffer(maxBuffer) {
629662
if (maxBuffer != null && !(typeof maxBuffer === 'number' && maxBuffer >= 0)) {
630-
throw new TypeError('"maxBuffer" must be a positive number');
663+
throw new errors.RangeError('ERR_VALUE_OUT_OF_RANGE',
664+
'options.maxBuffer',
665+
'a positive number',
666+
maxBuffer);
631667
}
632668
}
633669

@@ -636,6 +672,9 @@ function sanitizeKillSignal(killSignal) {
636672
if (typeof killSignal === 'string' || typeof killSignal === 'number') {
637673
return convertToValidSignal(killSignal);
638674
} else if (killSignal != null) {
639-
throw new TypeError('"killSignal" must be a string or number');
675+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
676+
'options.killSignal',
677+
['string', 'number'],
678+
killSignal);
640679
}
641680
}

lib/internal/errors.js

+3
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,9 @@ E('ERR_BUFFER_TOO_LARGE',
272272
`Cannot create a Buffer larger than 0x${kMaxLength.toString(16)} bytes`);
273273
E('ERR_CANNOT_WATCH_SIGINT', 'Cannot watch for SIGINT signals');
274274
E('ERR_CHILD_CLOSED_BEFORE_REPLY', 'Child closed before reply received');
275+
E('ERR_CHILD_PROCESS_IPC_REQUIRED',
276+
"Forked processes must have an IPC channel, missing value 'ipc' in %s");
277+
E('ERR_CHILD_PROCESS_STDIO_MAXBUFFER', '%s maxBuffer length exceeded');
275278
E('ERR_CONSOLE_WRITABLE_STREAM',
276279
'Console expects a writable stream instance for %s');
277280
E('ERR_CPU_USAGE', 'Unable to obtain cpu usage %s');

test/parallel/test-child-process-exec-maxBuffer.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ const cp = require('child_process');
55

66
function checkFactory(streamName) {
77
return common.mustCall((err) => {
8-
const message = `${streamName} maxBuffer exceeded`;
9-
assert.strictEqual(err.message, message);
8+
assert.strictEqual(err.message, `${streamName} maxBuffer length exceeded`);
9+
assert(err instanceof RangeError);
10+
assert.strictEqual(err.code, 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER');
1011
});
1112
}
1213

test/parallel/test-child-process-fork-stdio-string-variant.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ const fork = require('child_process').fork;
1010
const fixtures = require('../common/fixtures');
1111

1212
const childScript = fixtures.path('child-process-spawn-node');
13-
const errorRegexp = /^TypeError: Incorrect value of stdio option:/;
1413
const malFormedOpts = { stdio: '33' };
1514
const payload = { hello: 'world' };
1615

17-
assert.throws(() => fork(childScript, malFormedOpts), errorRegexp);
16+
common.expectsError(
17+
() => fork(childScript, malFormedOpts),
18+
{ code: 'ERR_INVALID_OPT_VALUE', type: TypeError });
1819

1920
function test(stringVariant) {
2021
const child = fork(childScript, { stdio: stringVariant });

test/parallel/test-child-process-fork-stdio.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ if (process.argv[2] === 'child') {
1919
process.send(data);
2020
});
2121
} else {
22-
assert.throws(() => {
23-
cp.fork(__filename, { stdio: ['pipe', 'pipe', 'pipe', 'pipe'] });
24-
}, /Forked processes must have an IPC channel/);
22+
common.expectsError(
23+
() => cp.fork(__filename, { stdio: ['pipe', 'pipe', 'pipe', 'pipe'] }),
24+
{ code: 'ERR_CHILD_PROCESS_IPC_REQUIRED', type: Error });
2525

2626
let ipc = '';
2727
let stderr = '';

test/parallel/test-child-process-spawn-typeerror.js

+31-29
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ const { spawn, fork, execFile } = require('child_process');
2626
const fixtures = require('../common/fixtures');
2727
const cmd = common.isWindows ? 'rundll32' : 'ls';
2828
const invalidcmd = 'hopefully_you_dont_have_this_on_your_machine';
29-
const invalidArgsMsg = /Incorrect value of args option/;
30-
const invalidOptionsMsg = /"options" argument must be an object/;
31-
const invalidFileMsg =
32-
/^TypeError: "file" argument must be a non-empty string$/;
3329

3430
const empty = fixtures.path('empty.js');
3531

32+
const invalidArgValueError =
33+
common.expectsError({ code: 'ERR_INVALID_ARG_VALUE', type: TypeError }, 13);
34+
35+
const invalidArgTypeError =
36+
common.expectsError({ code: 'ERR_INVALID_ARG_TYPE', type: TypeError }, 11);
37+
3638
assert.throws(function() {
3739
const child = spawn(invalidcmd, 'this is not an array');
3840
child.on('error', common.mustNotCall());
@@ -58,32 +60,32 @@ assert.doesNotThrow(function() {
5860
// verify that invalid argument combinations throw
5961
assert.throws(function() {
6062
spawn();
61-
}, invalidFileMsg);
63+
}, invalidArgTypeError);
6264

6365
assert.throws(function() {
6466
spawn('');
65-
}, invalidFileMsg);
67+
}, invalidArgTypeError);
6668

6769
assert.throws(function() {
68-
const file = { toString() { throw new Error('foo'); } };
70+
const file = { toString() { return null; } };
6971
spawn(file);
70-
}, invalidFileMsg);
72+
}, invalidArgTypeError);
7173

7274
assert.throws(function() {
7375
spawn(cmd, null);
74-
}, invalidArgsMsg);
76+
}, invalidArgTypeError);
7577

7678
assert.throws(function() {
7779
spawn(cmd, true);
78-
}, invalidArgsMsg);
80+
}, invalidArgTypeError);
7981

8082
assert.throws(function() {
8183
spawn(cmd, [], null);
82-
}, invalidOptionsMsg);
84+
}, invalidArgTypeError);
8385

8486
assert.throws(function() {
8587
spawn(cmd, [], 1);
86-
}, invalidOptionsMsg);
88+
}, invalidArgTypeError);
8789

8890
// Argument types for combinatorics
8991
const a = [];
@@ -107,11 +109,11 @@ assert.doesNotThrow(function() { spawn(cmd, o); });
107109
assert.doesNotThrow(function() { spawn(cmd, u, o); });
108110
assert.doesNotThrow(function() { spawn(cmd, a, u); });
109111

110-
assert.throws(function() { spawn(cmd, n, o); }, TypeError);
111-
assert.throws(function() { spawn(cmd, a, n); }, TypeError);
112+
assert.throws(function() { spawn(cmd, n, o); }, invalidArgTypeError);
113+
assert.throws(function() { spawn(cmd, a, n); }, invalidArgTypeError);
112114

113-
assert.throws(function() { spawn(cmd, s); }, TypeError);
114-
assert.throws(function() { spawn(cmd, a, s); }, TypeError);
115+
assert.throws(function() { spawn(cmd, s); }, invalidArgTypeError);
116+
assert.throws(function() { spawn(cmd, a, s); }, invalidArgTypeError);
115117

116118

117119
// verify that execFile has same argument parsing behaviour as spawn
@@ -160,17 +162,17 @@ assert.doesNotThrow(function() { execFile(cmd, c, n); });
160162
// string is invalid in arg position (this may seem strange, but is
161163
// consistent across node API, cf. `net.createServer('not options', 'not
162164
// callback')`
163-
assert.throws(function() { execFile(cmd, s, o, c); }, TypeError);
164-
assert.throws(function() { execFile(cmd, a, s, c); }, TypeError);
165-
assert.throws(function() { execFile(cmd, a, o, s); }, TypeError);
166-
assert.throws(function() { execFile(cmd, a, s); }, TypeError);
167-
assert.throws(function() { execFile(cmd, o, s); }, TypeError);
168-
assert.throws(function() { execFile(cmd, u, u, s); }, TypeError);
169-
assert.throws(function() { execFile(cmd, n, n, s); }, TypeError);
170-
assert.throws(function() { execFile(cmd, a, u, s); }, TypeError);
171-
assert.throws(function() { execFile(cmd, a, n, s); }, TypeError);
172-
assert.throws(function() { execFile(cmd, u, o, s); }, TypeError);
173-
assert.throws(function() { execFile(cmd, n, o, s); }, TypeError);
165+
assert.throws(function() { execFile(cmd, s, o, c); }, invalidArgValueError);
166+
assert.throws(function() { execFile(cmd, a, s, c); }, invalidArgValueError);
167+
assert.throws(function() { execFile(cmd, a, o, s); }, invalidArgValueError);
168+
assert.throws(function() { execFile(cmd, a, s); }, invalidArgValueError);
169+
assert.throws(function() { execFile(cmd, o, s); }, invalidArgValueError);
170+
assert.throws(function() { execFile(cmd, u, u, s); }, invalidArgValueError);
171+
assert.throws(function() { execFile(cmd, n, n, s); }, invalidArgValueError);
172+
assert.throws(function() { execFile(cmd, a, u, s); }, invalidArgValueError);
173+
assert.throws(function() { execFile(cmd, a, n, s); }, invalidArgValueError);
174+
assert.throws(function() { execFile(cmd, u, o, s); }, invalidArgValueError);
175+
assert.throws(function() { execFile(cmd, n, o, s); }, invalidArgValueError);
174176
assert.doesNotThrow(function() { execFile(cmd, c, s); });
175177

176178

@@ -192,5 +194,5 @@ assert.doesNotThrow(function() { fork(empty, n, n); });
192194
assert.doesNotThrow(function() { fork(empty, n, o); });
193195
assert.doesNotThrow(function() { fork(empty, a, n); });
194196

195-
assert.throws(function() { fork(empty, s); }, TypeError);
196-
assert.throws(function() { fork(empty, a, s); }, TypeError);
197+
assert.throws(function() { fork(empty, s); }, invalidArgValueError);
198+
assert.throws(function() { fork(empty, a, s); }, invalidArgValueError);

0 commit comments

Comments
 (0)