Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
errors, child_process: migrate to using internal/errors
  • Loading branch information
jasnell committed Apr 27, 2017
commit 14cacc667bed860652707bda947d546bcee4086e
61 changes: 61 additions & 0 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,27 @@ Node.js API that consumes `file:` URLs (such as certain functions in the
[`fs`][] module) encounters a file URL with an incompatible path. The exact
semantics for determining whether a path can be used is platform-dependent.

<a id="ERR_INVALID_HANDLE_TYPE"></a>
### ERR_INVALID_HANDLE_TYPE

The '`ERR_INVALID_HANDLE_TYPE`' error code is used when an attempt is made to
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto. ERR_IPC_INVALID_HANDLE_TYPE

send an unsupported "handle" over an IPC communication channel to a child
process. See [`child.send()`] and [`process.send()`] for more information.

<a id="ERR_INVALID_OPT_VALUE"></a>
### ERR_INVALID_OPT_VALUE

The `'ERR_INVALID_OPT_VALUE'` error code is used generically to identify when
an invalid or unexpected value has been passed in an options object.

<a id="ERR_INVALID_SYNC_FORK_INPUT"></a>
### ERR_INVALID_SYNC_FORK_INPUT

The `'ERR_INVALID_SYNC_FORK_INPUT'` error code is used when a `Buffer`,
`Uint8Array` or `string` is provided as stdio input to a synchronous
fork. See the documentation for the [`child_process`](child_process.html)
module for more information.

<a id="ERR_INVALID_THIS"></a>
### ERR_INVALID_THIS

Expand Down Expand Up @@ -642,6 +663,36 @@ It is currently only used in the [WHATWG URL API][] support in the [`fs`][]
module (which only accepts URLs with `'file'` scheme), but may be used in other
Node.js APIs as well in the future.

<a id="ERR_IPC_CHANNEL_CLOSED"></a>
### ERR_IPC_CHANNEL_CLOSED

The `'ERR_IPC_CHANNEL_CLOSED'` error code is used when an attempt is made to use
an IPC communication channel that has already been closed.

<a id="ERR_IPC_DISCONNECTED"></a>
### ERR_IPC_DISCONNECTED

The `'ERR_IPC_DISCONNECTED'` error code is used when an attempt is made to
disconnect an already disconnected IPC communication channel between two
Node.js processes. See the documentation for the
[`child_process`](child_process.html) module for more information.

<a id="ERR_IPC_ONE_PIPE"></a>
### ERR_IPC_ONE_PIPE

The `'ERR_IPC_ONE_PIPE'` error code is used when an attempt is made to create
a child Node.js process using more than one IPC communication channel.
See the documentation for the [`child_process`](child_process.html)
module for more information.

<a id="ERR_IPC_SYNC_FORK"></a>
### ERR_IPC_SYNC_FORK

The `'ERR_IPC_SYNC_FORK'` error code is used when an attempt is made to open
an IPC communication channel with a synchronous forked Node.js process.
See the documentation for the [`child_process`](child_process.html)
module for more information.

<a id="ERR_MISSING_ARGS"></a>
### ERR_MISSING_ARGS

Expand Down Expand Up @@ -674,6 +725,13 @@ kind of internal Node.js error that should not typically be triggered by user
code. Instances of this error point to an internal bug within the Node.js
binary itself.

<a id="ERR_UNKNOWN_SIGNAL"></a>
### ERR_UNKNOWN_SIGNAL

The `'ERR_UNKNOWN_SIGNAL`' error code is used when an invalid or unknown
process signal is passed to an API expecting a valid signal (such as
[`child.kill()`][]).

<a id="ERR_UNKNOWN_STDIN_TYPE"></a>
### ERR_UNKNOWN_STDIN_TYPE

Expand All @@ -693,6 +751,9 @@ in user code, although it is not impossible. Occurrences of this error are most
likely an indication of a bug within Node.js itself.


[`child.kill()`]: child_process.html#child_process_child_kill_signal
[`child.send()`]: child_process.html#child_process_child_send_message_sendhandle_options_callback
[`process.send()`]: process.html#process_process_send_message_sendhandle_options_callback
[`fs.readdir`]: fs.html#fs_fs_readdir_path_options_callback
[`fs.readFileSync`]: fs.html#fs_fs_readfilesync_file_options
[`fs.unlink`]: fs.html#fs_fs_unlink_path_callback
Expand Down
32 changes: 17 additions & 15 deletions lib/internal/child_process.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict';

const errors = require('internal/errors');
const StringDecoder = require('string_decoder').StringDecoder;
const EventEmitter = require('events');
const net = require('net');
Expand Down Expand Up @@ -367,6 +368,7 @@ function onErrorNT(self, err) {


ChildProcess.prototype.kill = function(sig) {

const signal = sig === 0 ? sig :
convertToValidSignal(sig === undefined ? 'SIGTERM' : sig);

Expand Down Expand Up @@ -538,15 +540,15 @@ function setupChannel(target, channel) {
options = undefined;
} else if (options !== undefined &&
(options === null || typeof options !== 'object')) {
throw new TypeError('"options" argument must be an object');
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'options', 'Object');
}

options = Object.assign({swallowErrors: false}, options);

if (this.connected) {
return this._send(message, handle, options, callback);
}
const ex = new Error('channel closed');
const ex = new errors.Error('ERR_IPC_CHANNEL_CLOSED');
if (typeof callback === 'function') {
process.nextTick(callback, ex);
} else {
Expand All @@ -559,7 +561,7 @@ function setupChannel(target, channel) {
assert(this.connected || this.channel);

if (message === undefined)
throw new TypeError('"message" argument cannot be undefined');
throw new errors.TypeError('ERR_MISSING_ARGS', 'message');

// Support legacy function signature
if (typeof options === 'boolean') {
Expand All @@ -586,7 +588,7 @@ function setupChannel(target, channel) {
} else if (handle instanceof UDP) {
message.type = 'dgram.Native';
} else {
throw new TypeError('This handle type can\'t be sent');
throw new errors.TypeError('ERR_INVALID_HANDLE_TYPE');
}

// Queue-up message and handle if we haven't received ACK yet.
Expand Down Expand Up @@ -686,7 +688,7 @@ function setupChannel(target, channel) {

target.disconnect = function() {
if (!this.connected) {
this.emit('error', new Error('IPC channel is already disconnected'));
this.emit('error', new errors.Error('ERR_IPC_DISCONNECTED'));
return;
}

Expand Down Expand Up @@ -766,11 +768,12 @@ function _validateStdio(stdio, sync) {
case 'ignore': stdio = ['ignore', 'ignore', 'ignore']; break;
case 'pipe': stdio = ['pipe', 'pipe', 'pipe']; break;
case 'inherit': stdio = [0, 1, 2]; break;
default: throw new TypeError('Incorrect value of stdio option: ' + stdio);
default:
throw new errors.TypeError('ERR_INVALID_OPT_VALUE', 'stdio', stdio);
}
} else if (!Array.isArray(stdio)) {
throw new TypeError('Incorrect value of stdio option: ' +
util.inspect(stdio));
throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
'stdio', util.inspect(stdio));
}

// At least 3 stdio will be created
Expand Down Expand Up @@ -812,9 +815,9 @@ function _validateStdio(stdio, sync) {
// Cleanup previously created pipes
cleanup();
if (!sync)
throw new Error('Child process can have only one IPC pipe');
throw new errors.Error('ERR_IPC_ONE_PIPE');
else
throw new Error('You cannot use IPC with synchronous forks');
throw new errors.Error('ERR_IPC_SYNC_FORK');
}

ipc = new Pipe(true);
Expand Down Expand Up @@ -849,15 +852,14 @@ function _validateStdio(stdio, sync) {
} else if (isUint8Array(stdio) || typeof stdio === 'string') {
if (!sync) {
cleanup();
throw new TypeError('Asynchronous forks do not support ' +
'Buffer, Uint8Array or string input: ' +
util.inspect(stdio));
throw new errors.TypeError('ERR_INVALID_SYNC_FORK_INPUT',
util.inspect(stdio));
}
} else {
// Cleanup
cleanup();
throw new TypeError('Incorrect value for stdio stream: ' +
util.inspect(stdio));
throw new errors.TypeError('ERR_INVALID_OPT_VALUE', 'stdio',
util.inspect(stdio));
}

return acc;
Expand Down
17 changes: 16 additions & 1 deletion lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,17 +91,32 @@ E('ERR_INVALID_ARG_TYPE', invalidArgType);
E('ERR_INVALID_CALLBACK', 'callback must be a function');
E('ERR_INVALID_FILE_URL_HOST', 'File URL host %s');
E('ERR_INVALID_FILE_URL_PATH', 'File URL path %s');
E('ERR_INVALID_HANDLE_TYPE', 'This handle type cannot be sent');
E('ERR_INVALID_OPT_VALUE',
(name, value) => {
return `The value "${String(value)}" is invalid for option "${name}"`;
});
E('ERR_INVALID_SYNC_FORK_INPUT',
(value) => {
return 'Asynchronous forks do not support Buffer, Uint8Array or string' +
`input: ${value}`;
});
E('ERR_INVALID_THIS', 'Value of "this" must be of type %s');
E('ERR_INVALID_TUPLE', '%s must be an iterable %s tuple');
E('ERR_INVALID_URL', 'Invalid URL: %s');
E('ERR_INVALID_URL_SCHEME',
(expected) => `The URL must be ${oneOf(expected, 'scheme')}`);
E('ERR_IPC_CHANNEL_CLOSED', 'channel closed');
E('ERR_IPC_DISCONNECTED', 'IPC channel is already disconnected');
E('ERR_IPC_ONE_PIPE', 'Child process can have only one IPC pipe');
E('ERR_IPC_SYNC_FORK', 'IPC cannot be used with synchronous forks');
E('ERR_MISSING_ARGS', missingArgs);
E('ERR_STDERR_CLOSE', 'process.stderr cannot be closed');
E('ERR_STDOUT_CLOSE', 'process.stdout cannot be closed');
E('ERR_UNKNOWN_BUILTIN_MODULE', (id) => `No such built-in module: ${id}`);
E('ERR_UNKNOWN_SIGNAL', (signal) => `Unknown signal: ${signal}`);
E('ERR_UNKNOWN_STDIN_TYPE', 'Unknown stdin file type');
E('ERR_UNKNOWN_STREAM_TYPE', 'Unknown stream file type');
E('ERR_UNKNOWN_BUILTIN_MODULE', (id) => `No such built-in module: ${id}`);
// Add new errors from here...

function invalidArgType(name, expected, actual) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I read it correctly, this would print The "options" argument must be type Object. It looks better to me as The "options" argument must be of type Object

Copy link
Copy Markdown
Member

@targos targos Mar 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to have some tests for this function OK, tests are in #11294

Expand Down
3 changes: 2 additions & 1 deletion lib/internal/util.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict';

const errors = require('internal/errors');
const binding = process.binding('util');
const signals = process.binding('constants').os.signals;

Expand Down Expand Up @@ -177,7 +178,7 @@ function convertToValidSignal(signal) {
if (signalName) return signalName;
}

throw new Error('Unknown signal: ' + signal);
throw new errors.Error('ERR_UNKNOWN_SIGNAL', signal);
}

function getConstructorOf(obj) {
Expand Down
4 changes: 2 additions & 2 deletions test/parallel/test-child-process-constructor.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

require('../common');
const common = require('../common');
const assert = require('assert');
const { ChildProcess } = require('child_process');
assert.strictEqual(typeof ChildProcess, 'function');
Expand Down Expand Up @@ -64,6 +64,6 @@ assert(Number.isInteger(child.pid));
// try killing with invalid signal
assert.throws(() => {
child.kill('foo');
}, /^Error: Unknown signal: foo$/);
}, common.expectsError({ code: 'ERR_UNKNOWN_SIGNAL' }));

assert.strictEqual(child.kill(), true);
2 changes: 1 addition & 1 deletion test/parallel/test-child-process-send-type-error.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const cp = require('child_process');
function fail(proc, args) {
assert.throws(() => {
proc.send.apply(proc, args);
}, /"options" argument must be an object/);
}, common.expectsError({code: 'ERR_INVALID_ARG_TYPE', type: TypeError}));
}

let target = process;
Expand Down
2 changes: 1 addition & 1 deletion test/parallel/test-child-process-spawnsync-kill-signal.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ if (process.argv[2] === 'child') {
// Verify that an error is thrown for unknown signals.
assert.throws(() => {
spawn('SIG_NOT_A_REAL_SIGNAL');
}, /Error: Unknown signal: SIG_NOT_A_REAL_SIGNAL/);
}, common.expectsError({ code: 'ERR_UNKNOWN_SIGNAL' }));

// Verify that the default kill signal is SIGTERM.
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ if (!common.isWindows) {
{
// Validate the killSignal option
const typeErr = /^TypeError: "killSignal" must be a string or number$/;
const unknownSignalErr = /^Error: Unknown signal:/;
const unknownSignalErr =
common.expectsError({ code: 'ERR_UNKNOWN_SIGNAL' });

pass('killSignal', undefined);
pass('killSignal', null);
Expand Down
2 changes: 1 addition & 1 deletion test/parallel/test-child-process-stdio.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ assert.deepStrictEqual(options, {stdio: 'ignore'});

assert.throws(() => {
common.spawnPwd({stdio: ['pipe', 'pipe', 'pipe', 'ipc', 'ipc']});
}, /^Error: Child process can have only one IPC pipe$/);
}, common.expectsError({code: 'ERR_IPC_ONE_PIPE', type: Error}));
18 changes: 8 additions & 10 deletions test/parallel/test-child-process-validate-stdio.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
'use strict';
// Flags: --expose_internals

require('../common');
const common = require('../common');
const assert = require('assert');
const _validateStdio = require('internal/child_process')._validateStdio;

const expectedError =
common.expectsError({code: 'ERR_INVALID_OPT_VALUE', type: TypeError});

// should throw if string and not ignore, pipe, or inherit
assert.throws(function() {
_validateStdio('foo');
}, /Incorrect value of stdio option/);
assert.throws(() => _validateStdio('foo'), expectedError);

// should throw if not a string or array
assert.throws(function() {
_validateStdio(600);
}, /Incorrect value of stdio option/);
assert.throws(() => _validateStdio(600), expectedError);

// should populate stdio with undefined if len < 3
{
Expand All @@ -27,9 +26,8 @@ assert.throws(function() {

// should throw if stdio has ipc and sync is true
const stdio2 = ['ipc', 'ipc', 'ipc'];
assert.throws(function() {
_validateStdio(stdio2, true);
}, /You cannot use IPC with synchronous forks/);
assert.throws(() => _validateStdio(stdio2, true),
common.expectsError({code: 'ERR_IPC_SYNC_FORK', type: Error}));

{
const stdio3 = [process.stdin, process.stdout, process.stderr];
Expand Down