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