Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

errors, process: port internal/process errors to internal/errors #11294

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
100 changes: 78 additions & 22 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ called.

All JavaScript errors are handled as exceptions that *immediately* generate
and throw an error using the standard JavaScript `throw` mechanism. These
are handled using the [`try / catch` construct][try-catch] provided by the JavaScript
language.
are handled using the [`try / catch` construct][try-catch] provided by the
JavaScript language.

```js
// Throws with a ReferenceError because z is undefined
Expand Down Expand Up @@ -105,8 +105,8 @@ pass or fail).

For *all* `EventEmitter` objects, if an `'error'` event handler is not
provided, the error will be thrown, causing the Node.js process to report an
unhandled exception and crash unless either: The [`domain`][domains] module is used
appropriately or a handler has been registered for the
unhandled exception and crash unless either: The [`domain`][domains] module is
used appropriately or a handler has been registered for the
[`process.on('uncaughtException')`][] event.

```js
Expand Down Expand Up @@ -255,15 +255,23 @@ will affect any stack trace captured *after* the value has been changed.
If set to a non-number value, or set to a negative number, stack traces will
not capture any frames.

### error.message
#### error.code

* {string}

The `error.code` property is a string label that identifies the kind of error.
See [Node.js Error Codes][] for details about specific codes.

#### error.message

* {string}

The `error.message` property is the string description of the error as set by calling `new Error(message)`.
The `message` passed to the constructor will also appear in the first line of
the stack trace of the `Error`, however changing this property after the
`Error` object is created *may not* change the first line of the stack trace
(for example, when `error.stack` is read before this property is changed).
The `error.message` property is the string description of the error as set by
calling `new Error(message)`. The `message` passed to the constructor will also
appear in the first line of the stack trace of the `Error`, however changing
this property after the `Error` object is created *may not* change the first
line of the stack trace (for example, when `error.stack` is read before this
property is changed).

```js
const err = new Error('The message');
Expand Down Expand Up @@ -451,18 +459,18 @@ added properties.

* {string}

The `error.code` property is a string representing the error code, which is always
`E` followed by a sequence of capital letters.
The `error.code` property is a string representing the error code, which is
typically `E` followed by a sequence of capital letters.

#### error.errno

* {string|number}

The `error.errno` property is a number or a string.
The number is a **negative** value which corresponds to the error code defined in
[`libuv Error handling`]. See uv-errno.h header file (`deps/uv/include/uv-errno.h` in
the Node.js source tree) for details.
In case of a string, it is the same as `error.code`.
The number is a **negative** value which corresponds to the error code defined
in [`libuv Error handling`]. See uv-errno.h header file
(`deps/uv/include/uv-errno.h` in the Node.js source tree) for details. In case
of a string, it is the same as `error.code`.

#### error.syscall

Expand All @@ -474,22 +482,22 @@ The `error.syscall` property is a string describing the [syscall][] that failed.

* {string}

When present (e.g. in `fs` or `child_process`), the `error.path` property is a string
containing a relevant invalid pathname.
When present (e.g. in `fs` or `child_process`), the `error.path` property is a
string containing a relevant invalid pathname.

#### error.address

* {string}

When present (e.g. in `net` or `dgram`), the `error.address` property is a string
describing the address to which the connection failed.
When present (e.g. in `net` or `dgram`), the `error.address` property is a
string describing the address to which the connection failed.

#### error.port

* {number}

When present (e.g. in `net` or `dgram`), the `error.port` property is a number representing
the connection's port that is not available.
When present (e.g. in `net` or `dgram`), the `error.port` property is a number
representing the connection's port that is not available.

### Common System Errors

Expand Down Expand Up @@ -550,6 +558,53 @@ found [here][online].
encountered by [`http`][] or [`net`][] -- often a sign that a `socket.end()`
was not properly called.

<a id="nodejs-error-codes"></a>
## Node.js Error Codes

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

The `'ERR_INVALID_ARG_TYPE'` error code is used generically to identify that
Copy link
Member

Choose a reason for hiding this comment

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

(Not sure which PR is the right place to ask, this one seems the oldest so I'll ask here)

Which one does the "type" here cover:

  • Primitive types (i.e. typeof types, doesn't differentiate Date and RegExp and stuff)
  • Builtin type tags(i.e. results of Object.prototype.toString.call(), differentiates Date and RegExp)
  • Class mismatch(i.e. instanceof types, differentiates Date and RegExp)

Copy link
Member

Choose a reason for hiding this comment

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

Also, some PRs use the typeof types as the expected type(e.g. "function"), some uses the class name/type tag(e.g. "Object"), we probably need to be a little bit consistent on this one.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is not handled very consistently throughout the Node.js source. Sometimes the value itself is passed in, sometimes the typeof is used, etc. The goal here would be to start getting some consistency but it's hard to nail down exactly what that should be.

Copy link
Member

Choose a reason for hiding this comment

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

Got it, so we should focus on migrating the errors to use the new system with a reasonable code right now, and deal with message later, because after the migration it would be easier to change them anyway?

an argument of the wrong type has been passed to a Node.js API.

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

The `'ERR_INVALID_CALLBACK'` error code is used generically to identify that
a callback function is required and has not been provided to a Node.js API.

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

An error using the `'ERR_STDERR_CLOSE'` code is thrown specifically when an
attempt is made to close the `process.stderr` stream. By design, Node.js does
not allow `stdout` or `stderr` Streams to be closed by user code.

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

An error using the `'ERR_STDOUT_CLOSE'` code is thrown specifically when an
attempt is made to close the `process.stdout` stream. By design, Node.js does
not allow `stdout` or `stderr` Streams to be closed by user code.

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

An error using the `'ERR_UNKNOWN_STDIN_TYPE'` code is thrown specifically when
an attempt is made to launch a Node.js process with an unknown `stdin` file
type. Errors of this kind cannot *typically* be caused by errors in user code,
although it is not impossible. Occurrences of this error are most likely an
indication of a bug within Node.js itself.

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

An error using the `'ERR_UNKNOWN_STREAM_TYPE'` code is thrown specifically when
an attempt is made to launch a Node.js process with an unknown `stdout` or
`stderr` file type. Errors of this kind cannot *typically* be caused by errors
in user code, although it is not impossible. Occurrences of this error are most
likely an indication of a bug within Node.js itself.

[`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 All @@ -562,6 +617,7 @@ found [here][online].
[domains]: domain.html
[event emitter-based]: events.html#events_class_eventemitter
[file descriptors]: https://en.wikipedia.org/wiki/File_descriptor
[Node.js Error Codes]: #nodejs-error-codes
[online]: http://man7.org/linux/man-pages/man3/errno.3.html
[stream-based]: stream.html
[syscall]: http://man7.org/linux/man-pages/man2/syscall.2.html
Expand Down
28 changes: 28 additions & 0 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,32 @@ module.exports = exports = {
//
// Note: Please try to keep these in alphabetical order
E('ERR_ASSERTION', (msg) => msg);
E('ERR_INVALID_ARG_TYPE', invalidArgType);
E('ERR_INVALID_CALLBACK', 'callback must be a function');
E('ERR_STDERR_CLOSE', 'process.stderr cannot be closed');
E('ERR_STDOUT_CLOSE', 'process.stdout cannot be closed');
E('ERR_UNKNOWN_STDIN_TYPE', 'Unknown stdin file type');
E('ERR_UNKNOWN_STREAM_TYPE', 'Unknown stream file type');
// Add new errors from here...

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

Choose a reason for hiding this comment

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

Copying my comment from #11300:

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

assert(name, 'name is required');
assert(expected, 'expected is required');
var msg = `The "${name}" argument must be `;
if (Array.isArray(expected)) {
var len = expected.length;
expected = expected.map((i) => String(i));
if (len > 1) {
msg += `one of type ${expected.slice(0, len - 1).join(', ')}, or ` +
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe break out the expected.slice().join() into a variable? It is getting a little hard to read imo.

expected[len - 1];
} else {
msg += `of type ${expected[0]}`;
}
} else {
msg += `of type ${String(expected)}`;
}
if (arguments.length >= 3) {
Copy link
Contributor

Choose a reason for hiding this comment

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

With only the first three arguments being used, maybe make this if (arguments.length === 3) {?

Feel free to disregard

msg += `. Received type ${actual !== null ? typeof actual : 'null'}`;
}
return msg;
}
3 changes: 2 additions & 1 deletion lib/internal/process/next_tick.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ exports.setup = setupNextTick;

function setupNextTick() {
const promises = require('internal/process/promises');
const errors = require('internal/errors');
const emitPendingUnhandledRejections = promises.setup(scheduleMicrotasks);
var nextTickQueue = [];
var microtasksScheduled = false;
Expand Down Expand Up @@ -139,7 +140,7 @@ function setupNextTick() {

function nextTick(callback) {
if (typeof callback !== 'function')
throw new TypeError('callback is not a function');
throw new errors.TypeError('ERR_INVALID_CALLBACK');
// on the way out, don't bother. it won't get fired anyway.
if (process._exiting)
return;
Expand Down
24 changes: 19 additions & 5 deletions lib/internal/process/stdio.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,25 @@

exports.setup = setupStdio;

var errors;

function lazyErrors() {
if (!errors)
errors = require('internal/errors');
return errors;
}

function setupStdio() {
var stdin, stdout, stderr;
var stdin;
var stdout;
var stderr;

function getStdout() {
if (stdout) return stdout;
stdout = createWritableStdioStream(1);
stdout.destroy = stdout.destroySoon = function(er) {
er = er || new Error('process.stdout cannot be closed.');
const errors = lazyErrors();
er = er || new errors.Error('ERR_STDOUT_CLOSE');
stdout.emit('error', er);
};
if (stdout.isTTY) {
Expand All @@ -22,7 +33,8 @@ function setupStdio() {
if (stderr) return stderr;
stderr = createWritableStdioStream(2);
stderr.destroy = stderr.destroySoon = function(er) {
er = er || new Error('process.stderr cannot be closed.');
const errors = lazyErrors();
er = er || new errors.Error('ERR_STDERR_CLOSE');
stderr.emit('error', er);
};
if (stderr.isTTY) {
Expand Down Expand Up @@ -79,7 +91,8 @@ function setupStdio() {

default:
// Probably an error on in uv_guess_handle()
throw new Error('Implement me. Unknown stdin file type!');
const errors = lazyErrors();
throw new errors.Error('ERR_UNKNOWN_STDIN_TYPE');
}

// For supporting legacy API we put the FD here.
Expand Down Expand Up @@ -163,7 +176,8 @@ function createWritableStdioStream(fd) {

default:
// Probably an error on in uv_guess_handle()
throw new Error('Implement me. Unknown stream file type!');
const errors = lazyErrors();
throw new errors.Error('ERR_UNKNOWN_STREAM_TYPE');
}

// For supporting legacy API we put the FD here.
Expand Down
15 changes: 12 additions & 3 deletions lib/internal/process/warning.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@ const prefix = `(${process.release.name}:${process.pid}) `;

exports.setup = setupProcessWarnings;

var errors;
var fs;
var cachedFd;
var acquiringFd = false;
function nop() {}

function lazyErrors() {
if (!errors)
errors = require('internal/errors');
return errors;
}

function lazyFs() {
if (!fs)
fs = require('fs');
Expand Down Expand Up @@ -105,6 +112,7 @@ function setupProcessWarnings() {
// process.emitWarning(error)
// process.emitWarning(str[, type[, code]][, ctor])
process.emitWarning = function(warning, type, code, ctor) {
const errors = lazyErrors();
if (typeof type === 'function') {
ctor = type;
code = undefined;
Expand All @@ -115,17 +123,18 @@ function setupProcessWarnings() {
code = undefined;
}
if (code !== undefined && typeof code !== 'string')
throw new TypeError('\'code\' must be a String');
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'code', 'string');
if (type !== undefined && typeof type !== 'string')
throw new TypeError('\'type\' must be a String');
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'type', 'string');
if (warning === undefined || typeof warning === 'string') {
warning = new Error(warning);
warning.name = String(type || 'Warning');
if (code !== undefined) warning.code = code;
Error.captureStackTrace(warning, ctor || process.emitWarning);
}
if (!(warning instanceof Error)) {
throw new TypeError('\'warning\' must be an Error object or string.');
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
'warning', ['Error', 'string']);
}
if (warning.name === 'DeprecationWarning') {
if (process.noDeprecation)
Expand Down
20 changes: 20 additions & 0 deletions test/parallel/test-internal-errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,23 @@ assert.throws(
() => errors.E('TEST_ERROR_USED_SYMBOL'),
/^AssertionError: Error symbol: TEST_ERROR_USED_SYMBOL was already used\.$/
);

// // Test ERR_INVALID_ARG_TYPE
assert.strictEqual(errors.message('ERR_INVALID_ARG_TYPE', ['a', 'b']),
'The "a" argument must be of type b');
assert.strictEqual(errors.message('ERR_INVALID_ARG_TYPE', ['a', ['b']]),
'The "a" argument must be of type b');
assert.strictEqual(errors.message('ERR_INVALID_ARG_TYPE', ['a', ['b', 'c']]),
'The "a" argument must be one of type b, or c');
assert.strictEqual(errors.message('ERR_INVALID_ARG_TYPE',
['a', ['b', 'c', 'd']]),
'The "a" argument must be one of type b, c, or d');
assert.strictEqual(errors.message('ERR_INVALID_ARG_TYPE', ['a', 'b', 'c']),
'The "a" argument must be of type b. Received type string');
assert.strictEqual(errors.message('ERR_INVALID_ARG_TYPE',
['a', 'b', undefined]),
'The "a" argument must be of type b. Received type ' +
'undefined');
assert.strictEqual(errors.message('ERR_INVALID_ARG_TYPE',
['a', 'b', null]),
'The "a" argument must be of type b. Received type null');
27 changes: 15 additions & 12 deletions test/parallel/test-process-emitwarning.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,19 @@ warningThrowToString.toString = function() {
};
process.emitWarning(warningThrowToString);

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

// TypeError is thrown on invalid input
assert.throws(() => process.emitWarning(1), TypeError);
assert.throws(() => process.emitWarning({}), TypeError);
assert.throws(() => process.emitWarning(true), TypeError);
assert.throws(() => process.emitWarning([]), TypeError);
assert.throws(() => process.emitWarning('', {}), TypeError);
assert.throws(() => process.emitWarning('', '', {}), TypeError);
assert.throws(() => process.emitWarning('', 1), TypeError);
assert.throws(() => process.emitWarning('', '', 1), TypeError);
assert.throws(() => process.emitWarning('', true), TypeError);
assert.throws(() => process.emitWarning('', '', true), TypeError);
assert.throws(() => process.emitWarning('', []), TypeError);
assert.throws(() => process.emitWarning('', '', []), TypeError);
assert.throws(() => process.emitWarning(1), expectedError);
assert.throws(() => process.emitWarning({}), expectedError);
assert.throws(() => process.emitWarning(true), expectedError);
assert.throws(() => process.emitWarning([]), expectedError);
assert.throws(() => process.emitWarning('', {}), expectedError);
assert.throws(() => process.emitWarning('', '', {}), expectedError);
assert.throws(() => process.emitWarning('', 1), expectedError);
assert.throws(() => process.emitWarning('', '', 1), expectedError);
assert.throws(() => process.emitWarning('', true), expectedError);
assert.throws(() => process.emitWarning('', '', true), expectedError);
assert.throws(() => process.emitWarning('', []), expectedError);
assert.throws(() => process.emitWarning('', '', []), expectedError);
Loading