• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Using the internal/errors.js module
2
3## What is internal/errors.js
4
5The `require('internal/errors')` module is an internal-only module that can be
6used to produce `Error`, `TypeError` and `RangeError` instances that use a
7static, permanent error code and an optionally parameterized message.
8
9The intent of the module is to allow errors provided by Node.js to be assigned a
10permanent identifier. Without a permanent identifier, userland code may need to
11inspect error messages to distinguish one error from another. An unfortunate
12result of that practice is that changes to error messages result in broken code
13in the ecosystem. For that reason, Node.js has considered error message changes
14to be breaking changes. By providing a permanent identifier for a specific
15error, we reduce the need for userland code to inspect error messages.
16
17Switching an existing error to use the `internal/errors` module must be
18considered a `semver-major` change.
19
20## Using internal/errors.js
21
22The `internal/errors` module exposes all custom errors as subclasses of the
23builtin errors. After being added, an error can be found in the `codes` object.
24
25For instance, an existing `Error` such as:
26
27```js
28const err = new TypeError(`Expected string received ${type}`);
29```
30
31Can be replaced by first adding a new error key into the `internal/errors.js`
32file:
33
34```js
35E('FOO', 'Expected string received %s', TypeError);
36```
37
38Then replacing the existing `new TypeError` in the code:
39
40```js
41const { FOO } = require('internal/errors').codes;
42// ...
43const err = new FOO(type);
44```
45
46## Adding new errors
47
48New static error codes are added by modifying the `internal/errors.js` file
49and appending the new error codes to the end using the utility `E()` method.
50
51```js
52E('EXAMPLE_KEY1', 'This is the error value', TypeError);
53E('EXAMPLE_KEY2', (a, b) => `${a} ${b}`, RangeError);
54```
55
56The first argument passed to `E()` is the static identifier. The second
57argument is either a String with optional `util.format()` style replacement
58tags (e.g. `%s`, `%d`), or a function returning a String. The optional
59additional arguments passed to the `errors.message()` function (which is
60used by the `errors.Error`, `errors.TypeError` and `errors.RangeError` classes),
61will be used to format the error message. The third argument is the base class
62that the new error will extend.
63
64It is possible to create multiple derived
65classes by providing additional arguments. The other ones will be exposed as
66properties of the main class:
67
68<!-- eslint-disable no-unreachable -->
69```js
70E('EXAMPLE_KEY', 'Error message', TypeError, RangeError);
71
72// In another module
73const { EXAMPLE_KEY } = require('internal/errors').codes;
74// TypeError
75throw new EXAMPLE_KEY();
76// RangeError
77throw new EXAMPLE_KEY.RangeError();
78```
79
80## Documenting new errors
81
82Whenever a new static error code is added and used, corresponding documentation
83for the error code should be added to the `doc/api/errors.md` file. This will
84give users a place to go to easily look up the meaning of individual error
85codes.
86
87## Testing new errors
88
89When adding a new error, corresponding test(s) for the error message
90formatting may also be required. If the message for the error is a
91constant string then no test is required for the error message formatting
92as we can trust the error helper implementation. An example of this kind of
93error would be:
94
95```js
96E('ERR_SOCKET_ALREADY_BOUND', 'Socket is already bound');
97```
98
99If the error message is not a constant string then tests to validate
100the formatting of the message based on the parameters used when
101creating the error should be added to
102`test/parallel/test-internal-errors.js`.  These tests should validate
103all of the different ways parameters can be used to generate the final
104message string. A simple example is:
105
106```js
107// Test ERR_TLS_CERT_ALTNAME_INVALID
108assert.strictEqual(
109  errors.message('ERR_TLS_CERT_ALTNAME_INVALID', ['altname']),
110  'Hostname/IP does not match certificate\'s altnames: altname');
111```
112
113In addition, there should also be tests which validate the use of the
114error based on where it is used in the codebase.  If the error message is
115static, these tests should only validate that the expected code is received
116and NOT validate the message.  This will reduce the amount of test change
117required when the message for an error changes.
118
119```js
120assert.throws(() => {
121  socket.bind();
122}, common.expectsError({
123  code: 'ERR_SOCKET_ALREADY_BOUND',
124  type: Error
125}));
126```
127
128Avoid changing the format of the message after the error has been created.
129If it does make sense to do this for some reason, then additional tests
130validating the formatting of the error message for those cases will
131likely be required.
132
133## API
134
135### Object: errors.codes
136
137Exposes all internal error classes to be used by Node.js APIs.
138
139### Method: errors.message(key, args)
140
141* `key` {string} The static error identifier
142* `args` {Array} Zero or more optional arguments passed as an Array
143* Returns: {string}
144
145Returns the formatted error message string for the given `key`.
146