diff --git a/doc/api/errors.md b/doc/api/errors.md
index 63d2e1c20d5346..c3c5e497d3afb4 100644
--- a/doc/api/errors.md
+++ b/doc/api/errors.md
@@ -1303,6 +1303,11 @@ An invalid HTTP token was supplied.
An IP address is not valid.
+
+### `ERR_INVALID_OPT_TYPE`
+
+An option of the wrong type was passed in an options object.
+
### `ERR_INVALID_OPT_VALUE`
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index 206724eacb6f52..14189cd26c6039 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -686,6 +686,114 @@ const fatalExceptionStackEnhancers = {
}
};
+const invalidValueTypeError = (name, expected, actual, valueType) => {
+ assert(typeof name === 'string', "'name' must be a string");
+ if (!ArrayIsArray(expected)) {
+ expected = [expected];
+ }
+
+ let msg = 'The ';
+ if (valueType !== undefined) {
+ msg += `"${name}" ${valueType} `;
+ } else if (name.endsWith(' argument')) {
+ // For cases like 'first argument'
+ msg += `${name} `;
+ } else {
+ const type = name.includes('.') ? 'property' : 'argument';
+ msg += `"${name}" ${type} `;
+ }
+ msg += 'must be ';
+
+ const types = [];
+ const instances = [];
+ const other = [];
+
+ for (const value of expected) {
+ assert(typeof value === 'string',
+ 'All expected entries have to be of type string');
+ if (kTypes.includes(value)) {
+ types.push(value.toLowerCase());
+ } else if (classRegExp.test(value)) {
+ instances.push(value);
+ } else {
+ assert(value !== 'object',
+ 'The value "object" should be written as "Object"');
+ other.push(value);
+ }
+ }
+
+ // Special handle `object` in case other instances are allowed to outline
+ // the differences between each other.
+ if (instances.length > 0) {
+ const pos = types.indexOf('object');
+ if (pos !== -1) {
+ types.splice(pos, 1);
+ instances.push('Object');
+ }
+ }
+
+ if (types.length > 0) {
+ if (types.length > 2) {
+ const last = types.pop();
+ msg += `one of type ${types.join(', ')}, or ${last}`;
+ } else if (types.length === 2) {
+ msg += `one of type ${types[0]} or ${types[1]}`;
+ } else {
+ msg += `of type ${types[0]}`;
+ }
+ if (instances.length > 0 || other.length > 0)
+ msg += ' or ';
+ }
+
+ if (instances.length > 0) {
+ if (instances.length > 2) {
+ const last = instances.pop();
+ msg += `an instance of ${instances.join(', ')}, or ${last}`;
+ } else {
+ msg += `an instance of ${instances[0]}`;
+ if (instances.length === 2) {
+ msg += ` or ${instances[1]}`;
+ }
+ }
+ if (other.length > 0)
+ msg += ' or ';
+ }
+
+ if (other.length > 0) {
+ if (other.length > 2) {
+ const last = other.pop();
+ msg += `one of ${other.join(', ')}, or ${last}`;
+ } else if (other.length === 2) {
+ msg += `one of ${other[0]} or ${other[1]}`;
+ } else {
+ if (other[0].toLowerCase() !== other[0])
+ msg += 'an ';
+ msg += `${other[0]}`;
+ }
+ }
+
+ if (actual == null) {
+ msg += `. Received ${actual}`;
+ } else if (typeof actual === 'function' && actual.name) {
+ msg += `. Received function ${actual.name}`;
+ } else if (typeof actual === 'object') {
+ if (actual.constructor && actual.constructor.name) {
+ msg += `. Received an instance of ${actual.constructor.name}`;
+ } else {
+ const inspected = lazyInternalUtilInspect()
+ .inspect(actual, { depth: -1 });
+ msg += `. Received ${inspected}`;
+ }
+ } else {
+ let inspected = lazyInternalUtilInspect()
+ .inspect(actual, { colors: false });
+ if (inspected.length > 25)
+ inspected = `${inspected.slice(0, 25)}...`;
+ msg += `. Received type ${typeof actual} (${inspected})`;
+ }
+ return msg;
+};
+
module.exports = {
addCodeToName, // Exported for NghttpError
codes,
@@ -931,112 +1039,9 @@ E('ERR_INVALID_ADDRESS_FAMILY', function(addressType, host, port) {
this.port = port;
return `Invalid address family: ${addressType} ${host}:${port}`;
}, RangeError);
-E('ERR_INVALID_ARG_TYPE',
- (name, expected, actual) => {
- assert(typeof name === 'string', "'name' must be a string");
- if (!ArrayIsArray(expected)) {
- expected = [expected];
- }
-
- let msg = 'The ';
- if (name.endsWith(' argument')) {
- // For cases like 'first argument'
- msg += `${name} `;
- } else {
- const type = name.includes('.') ? 'property' : 'argument';
- msg += `"${name}" ${type} `;
- }
- msg += 'must be ';
-
- const types = [];
- const instances = [];
- const other = [];
-
- for (const value of expected) {
- assert(typeof value === 'string',
- 'All expected entries have to be of type string');
- if (kTypes.includes(value)) {
- types.push(value.toLowerCase());
- } else if (classRegExp.test(value)) {
- instances.push(value);
- } else {
- assert(value !== 'object',
- 'The value "object" should be written as "Object"');
- other.push(value);
- }
- }
-
- // Special handle `object` in case other instances are allowed to outline
- // the differences between each other.
- if (instances.length > 0) {
- const pos = types.indexOf('object');
- if (pos !== -1) {
- types.splice(pos, 1);
- instances.push('Object');
- }
- }
-
- if (types.length > 0) {
- if (types.length > 2) {
- const last = types.pop();
- msg += `one of type ${types.join(', ')}, or ${last}`;
- } else if (types.length === 2) {
- msg += `one of type ${types[0]} or ${types[1]}`;
- } else {
- msg += `of type ${types[0]}`;
- }
- if (instances.length > 0 || other.length > 0)
- msg += ' or ';
- }
-
- if (instances.length > 0) {
- if (instances.length > 2) {
- const last = instances.pop();
- msg += `an instance of ${instances.join(', ')}, or ${last}`;
- } else {
- msg += `an instance of ${instances[0]}`;
- if (instances.length === 2) {
- msg += ` or ${instances[1]}`;
- }
- }
- if (other.length > 0)
- msg += ' or ';
- }
-
- if (other.length > 0) {
- if (other.length > 2) {
- const last = other.pop();
- msg += `one of ${other.join(', ')}, or ${last}`;
- } else if (other.length === 2) {
- msg += `one of ${other[0]} or ${other[1]}`;
- } else {
- if (other[0].toLowerCase() !== other[0])
- msg += 'an ';
- msg += `${other[0]}`;
- }
- }
-
- if (actual == null) {
- msg += `. Received ${actual}`;
- } else if (typeof actual === 'function' && actual.name) {
- msg += `. Received function ${actual.name}`;
- } else if (typeof actual === 'object') {
- if (actual.constructor && actual.constructor.name) {
- msg += `. Received an instance of ${actual.constructor.name}`;
- } else {
- const inspected = lazyInternalUtilInspect()
- .inspect(actual, { depth: -1 });
- msg += `. Received ${inspected}`;
- }
- } else {
- let inspected = lazyInternalUtilInspect()
- .inspect(actual, { colors: false });
- if (inspected.length > 25)
- inspected = `${inspected.slice(0, 25)}...`;
- msg += `. Received type ${typeof actual} (${inspected})`;
- }
- return msg;
- }, TypeError);
+E('ERR_INVALID_ARG_TYPE', (name, expected, actual) =>
+ invalidValueTypeError(name, expected, actual),
+ TypeError);
E('ERR_INVALID_ARG_VALUE', (name, value, reason = 'is invalid') => {
let inspected = lazyInternalUtilInspect().inspect(value);
if (inspected.length > 128) {
@@ -1070,6 +1075,9 @@ E('ERR_INVALID_FILE_URL_PATH', 'File URL path %s', TypeError);
E('ERR_INVALID_HANDLE_TYPE', 'This handle type cannot be sent', TypeError);
E('ERR_INVALID_HTTP_TOKEN', '%s must be a valid HTTP token ["%s"]', TypeError);
E('ERR_INVALID_IP_ADDRESS', 'Invalid IP address: %s', TypeError);
+E('ERR_INVALID_OPT_TYPE', (name, expected, actual) =>
+ invalidValueTypeError(name, expected, actual, 'option'),
+ TypeError);
E('ERR_INVALID_OPT_VALUE', (name, value) =>
`The value "${String(value)}" is invalid for option "${name}"`,
TypeError,
diff --git a/lib/internal/validators.js b/lib/internal/validators.js
index 329410ef3d8d0a..8d8e12ebea300a 100644
--- a/lib/internal/validators.js
+++ b/lib/internal/validators.js
@@ -11,6 +11,8 @@ const {
codes: {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
+ ERR_INVALID_OPT_TYPE,
+ ERR_INVALID_OPT_VALUE,
ERR_OUT_OF_RANGE,
ERR_UNKNOWN_SIGNAL
}
@@ -67,65 +69,84 @@ function parseFileMode(value, name, def) {
throw new ERR_INVALID_ARG_VALUE(name, value, modeDesc);
}
+const handleValueName = (name) => {
+ let TypeErr = ERR_INVALID_ARG_TYPE;
+ let ValueErr = ERR_INVALID_ARG_VALUE;
+ if (typeof name === 'object') {
+ if (name.type === 'option') {
+ TypeErr = ERR_INVALID_OPT_TYPE;
+ ValueErr = ERR_INVALID_OPT_VALUE;
+ }
+ name = name.name;
+ }
+ return { valueName: name, TypeErr, ValueErr };
+};
+
const validateInteger = hideStackFrames(
(value, name, min = NumberMIN_SAFE_INTEGER, max = NumberMAX_SAFE_INTEGER) => {
+ const { valueName, TypeErr } = handleValueName(name);
if (typeof value !== 'number')
- throw new ERR_INVALID_ARG_TYPE(name, 'number', value);
+ throw new TypeErr(valueName, 'number', value);
if (!NumberIsInteger(value))
- throw new ERR_OUT_OF_RANGE(name, 'an integer', value);
+ throw new ERR_OUT_OF_RANGE(valueName, 'an integer', value);
if (value < min || value > max)
- throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value);
+ throw new ERR_OUT_OF_RANGE(valueName, `>= ${min} && <= ${max}`, value);
}
);
const validateInt32 = hideStackFrames(
(value, name, min = -2147483648, max = 2147483647) => {
// The defaults for min and max correspond to the limits of 32-bit integers.
+ const { valueName, TypeErr } = handleValueName(name);
if (!isInt32(value)) {
if (typeof value !== 'number') {
- throw new ERR_INVALID_ARG_TYPE(name, 'number', value);
+ throw new TypeErr(valueName, 'number', value);
}
if (!NumberIsInteger(value)) {
- throw new ERR_OUT_OF_RANGE(name, 'an integer', value);
+ throw new ERR_OUT_OF_RANGE(valueName, 'an integer', value);
}
- throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value);
+ throw new ERR_OUT_OF_RANGE(valueName, `>= ${min} && <= ${max}`, value);
}
if (value < min || value > max) {
- throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value);
+ throw new ERR_OUT_OF_RANGE(valueName, `>= ${min} && <= ${max}`, value);
}
}
);
const validateUint32 = hideStackFrames((value, name, positive) => {
+ const { valueName, TypeErr } = handleValueName(name);
if (!isUint32(value)) {
if (typeof value !== 'number') {
- throw new ERR_INVALID_ARG_TYPE(name, 'number', value);
+ throw new TypeErr(valueName, 'number', value);
}
if (!NumberIsInteger(value)) {
- throw new ERR_OUT_OF_RANGE(name, 'an integer', value);
+ throw new ERR_OUT_OF_RANGE(valueName, 'an integer', value);
}
const min = positive ? 1 : 0;
// 2 ** 32 === 4294967296
- throw new ERR_OUT_OF_RANGE(name, `>= ${min} && < 4294967296`, value);
+ throw new ERR_OUT_OF_RANGE(valueName, `>= ${min} && < 4294967296`, value);
}
if (positive && value === 0) {
- throw new ERR_OUT_OF_RANGE(name, '>= 1 && < 4294967296', value);
+ throw new ERR_OUT_OF_RANGE(valueName, '>= 1 && < 4294967296', value);
}
});
function validateString(value, name) {
+ const { valueName, TypeErr } = handleValueName(name);
if (typeof value !== 'string')
- throw new ERR_INVALID_ARG_TYPE(name, 'string', value);
+ throw new TypeErr(valueName, 'string', value);
}
function validateNumber(value, name) {
+ const { valueName, TypeErr } = handleValueName(name);
if (typeof value !== 'number')
- throw new ERR_INVALID_ARG_TYPE(name, 'number', value);
+ throw new TypeErr(valueName, 'number', value);
}
function validateSignalName(signal, name = 'signal') {
+ const { valueName, TypeErr } = handleValueName(name);
if (typeof signal !== 'string')
- throw new ERR_INVALID_ARG_TYPE(name, 'string', signal);
+ throw new TypeErr(valueName, 'string', signal);
if (signals[signal] === undefined) {
if (signals[signal.toUpperCase()] !== undefined) {
@@ -138,11 +159,9 @@ function validateSignalName(signal, name = 'signal') {
}
const validateBuffer = hideStackFrames((buffer, name = 'buffer') => {
- if (!isArrayBufferView(buffer)) {
- throw new ERR_INVALID_ARG_TYPE(name,
- ['Buffer', 'TypedArray', 'DataView'],
- buffer);
- }
+ const { valueName, TypeErr } = handleValueName(name);
+ if (!isArrayBufferView(buffer))
+ throw new TypeErr(valueName, ['Buffer', 'TypedArray', 'DataView'], buffer);
});
function validateEncoding(data, encoding) {