errors.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. 'use strict'
  2. const { format, inspect } = require('./util/inspect')
  3. const { AggregateError: CustomAggregateError } = require('./primordials')
  4. /*
  5. This file is a reduced and adapted version of the main lib/internal/errors.js file defined at
  6. https://github.com/nodejs/node/blob/main/lib/internal/errors.js
  7. Don't try to replace with the original file and keep it up to date (starting from E(...) definitions)
  8. with the upstream file.
  9. */
  10. const AggregateError = globalThis.AggregateError || CustomAggregateError
  11. const kIsNodeError = Symbol('kIsNodeError')
  12. const kTypes = [
  13. 'string',
  14. 'function',
  15. 'number',
  16. 'object',
  17. // Accept 'Function' and 'Object' as alternative to the lower cased version.
  18. 'Function',
  19. 'Object',
  20. 'boolean',
  21. 'bigint',
  22. 'symbol'
  23. ]
  24. const classRegExp = /^([A-Z][a-z0-9]*)+$/
  25. const nodeInternalPrefix = '__node_internal_'
  26. const codes = {}
  27. function assert(value, message) {
  28. if (!value) {
  29. throw new codes.ERR_INTERNAL_ASSERTION(message)
  30. }
  31. }
  32. // Only use this for integers! Decimal numbers do not work with this function.
  33. function addNumericalSeparator(val) {
  34. let res = ''
  35. let i = val.length
  36. const start = val[0] === '-' ? 1 : 0
  37. for (; i >= start + 4; i -= 3) {
  38. res = `_${val.slice(i - 3, i)}${res}`
  39. }
  40. return `${val.slice(0, i)}${res}`
  41. }
  42. function getMessage(key, msg, args) {
  43. if (typeof msg === 'function') {
  44. assert(
  45. msg.length <= args.length,
  46. // Default options do not count.
  47. `Code: ${key}; The provided arguments length (${args.length}) does not match the required ones (${msg.length}).`
  48. )
  49. return msg(...args)
  50. }
  51. const expectedLength = (msg.match(/%[dfijoOs]/g) || []).length
  52. assert(
  53. expectedLength === args.length,
  54. `Code: ${key}; The provided arguments length (${args.length}) does not match the required ones (${expectedLength}).`
  55. )
  56. if (args.length === 0) {
  57. return msg
  58. }
  59. return format(msg, ...args)
  60. }
  61. function E(code, message, Base) {
  62. if (!Base) {
  63. Base = Error
  64. }
  65. class NodeError extends Base {
  66. constructor(...args) {
  67. super(getMessage(code, message, args))
  68. }
  69. toString() {
  70. return `${this.name} [${code}]: ${this.message}`
  71. }
  72. }
  73. Object.defineProperties(NodeError.prototype, {
  74. name: {
  75. value: Base.name,
  76. writable: true,
  77. enumerable: false,
  78. configurable: true
  79. },
  80. toString: {
  81. value() {
  82. return `${this.name} [${code}]: ${this.message}`
  83. },
  84. writable: true,
  85. enumerable: false,
  86. configurable: true
  87. }
  88. })
  89. NodeError.prototype.code = code
  90. NodeError.prototype[kIsNodeError] = true
  91. codes[code] = NodeError
  92. }
  93. function hideStackFrames(fn) {
  94. // We rename the functions that will be hidden to cut off the stacktrace
  95. // at the outermost one
  96. const hidden = nodeInternalPrefix + fn.name
  97. Object.defineProperty(fn, 'name', {
  98. value: hidden
  99. })
  100. return fn
  101. }
  102. function aggregateTwoErrors(innerError, outerError) {
  103. if (innerError && outerError && innerError !== outerError) {
  104. if (Array.isArray(outerError.errors)) {
  105. // If `outerError` is already an `AggregateError`.
  106. outerError.errors.push(innerError)
  107. return outerError
  108. }
  109. const err = new AggregateError([outerError, innerError], outerError.message)
  110. err.code = outerError.code
  111. return err
  112. }
  113. return innerError || outerError
  114. }
  115. class AbortError extends Error {
  116. constructor(message = 'The operation was aborted', options = undefined) {
  117. if (options !== undefined && typeof options !== 'object') {
  118. throw new codes.ERR_INVALID_ARG_TYPE('options', 'Object', options)
  119. }
  120. super(message, options)
  121. this.code = 'ABORT_ERR'
  122. this.name = 'AbortError'
  123. }
  124. }
  125. E('ERR_ASSERTION', '%s', Error)
  126. E(
  127. 'ERR_INVALID_ARG_TYPE',
  128. (name, expected, actual) => {
  129. assert(typeof name === 'string', "'name' must be a string")
  130. if (!Array.isArray(expected)) {
  131. expected = [expected]
  132. }
  133. let msg = 'The '
  134. if (name.endsWith(' argument')) {
  135. // For cases like 'first argument'
  136. msg += `${name} `
  137. } else {
  138. msg += `"${name}" ${name.includes('.') ? 'property' : 'argument'} `
  139. }
  140. msg += 'must be '
  141. const types = []
  142. const instances = []
  143. const other = []
  144. for (const value of expected) {
  145. assert(typeof value === 'string', 'All expected entries have to be of type string')
  146. if (kTypes.includes(value)) {
  147. types.push(value.toLowerCase())
  148. } else if (classRegExp.test(value)) {
  149. instances.push(value)
  150. } else {
  151. assert(value !== 'object', 'The value "object" should be written as "Object"')
  152. other.push(value)
  153. }
  154. }
  155. // Special handle `object` in case other instances are allowed to outline
  156. // the differences between each other.
  157. if (instances.length > 0) {
  158. const pos = types.indexOf('object')
  159. if (pos !== -1) {
  160. types.splice(types, pos, 1)
  161. instances.push('Object')
  162. }
  163. }
  164. if (types.length > 0) {
  165. switch (types.length) {
  166. case 1:
  167. msg += `of type ${types[0]}`
  168. break
  169. case 2:
  170. msg += `one of type ${types[0]} or ${types[1]}`
  171. break
  172. default: {
  173. const last = types.pop()
  174. msg += `one of type ${types.join(', ')}, or ${last}`
  175. }
  176. }
  177. if (instances.length > 0 || other.length > 0) {
  178. msg += ' or '
  179. }
  180. }
  181. if (instances.length > 0) {
  182. switch (instances.length) {
  183. case 1:
  184. msg += `an instance of ${instances[0]}`
  185. break
  186. case 2:
  187. msg += `an instance of ${instances[0]} or ${instances[1]}`
  188. break
  189. default: {
  190. const last = instances.pop()
  191. msg += `an instance of ${instances.join(', ')}, or ${last}`
  192. }
  193. }
  194. if (other.length > 0) {
  195. msg += ' or '
  196. }
  197. }
  198. switch (other.length) {
  199. case 0:
  200. break
  201. case 1:
  202. if (other[0].toLowerCase() !== other[0]) {
  203. msg += 'an '
  204. }
  205. msg += `${other[0]}`
  206. break
  207. case 2:
  208. msg += `one of ${other[0]} or ${other[1]}`
  209. break
  210. default: {
  211. const last = other.pop()
  212. msg += `one of ${other.join(', ')}, or ${last}`
  213. }
  214. }
  215. if (actual == null) {
  216. msg += `. Received ${actual}`
  217. } else if (typeof actual === 'function' && actual.name) {
  218. msg += `. Received function ${actual.name}`
  219. } else if (typeof actual === 'object') {
  220. var _actual$constructor
  221. if (
  222. (_actual$constructor = actual.constructor) !== null &&
  223. _actual$constructor !== undefined &&
  224. _actual$constructor.name
  225. ) {
  226. msg += `. Received an instance of ${actual.constructor.name}`
  227. } else {
  228. const inspected = inspect(actual, {
  229. depth: -1
  230. })
  231. msg += `. Received ${inspected}`
  232. }
  233. } else {
  234. let inspected = inspect(actual, {
  235. colors: false
  236. })
  237. if (inspected.length > 25) {
  238. inspected = `${inspected.slice(0, 25)}...`
  239. }
  240. msg += `. Received type ${typeof actual} (${inspected})`
  241. }
  242. return msg
  243. },
  244. TypeError
  245. )
  246. E(
  247. 'ERR_INVALID_ARG_VALUE',
  248. (name, value, reason = 'is invalid') => {
  249. let inspected = inspect(value)
  250. if (inspected.length > 128) {
  251. inspected = inspected.slice(0, 128) + '...'
  252. }
  253. const type = name.includes('.') ? 'property' : 'argument'
  254. return `The ${type} '${name}' ${reason}. Received ${inspected}`
  255. },
  256. TypeError
  257. )
  258. E(
  259. 'ERR_INVALID_RETURN_VALUE',
  260. (input, name, value) => {
  261. var _value$constructor
  262. const type =
  263. value !== null &&
  264. value !== undefined &&
  265. (_value$constructor = value.constructor) !== null &&
  266. _value$constructor !== undefined &&
  267. _value$constructor.name
  268. ? `instance of ${value.constructor.name}`
  269. : `type ${typeof value}`
  270. return `Expected ${input} to be returned from the "${name}"` + ` function but got ${type}.`
  271. },
  272. TypeError
  273. )
  274. E(
  275. 'ERR_MISSING_ARGS',
  276. (...args) => {
  277. assert(args.length > 0, 'At least one arg needs to be specified')
  278. let msg
  279. const len = args.length
  280. args = (Array.isArray(args) ? args : [args]).map((a) => `"${a}"`).join(' or ')
  281. switch (len) {
  282. case 1:
  283. msg += `The ${args[0]} argument`
  284. break
  285. case 2:
  286. msg += `The ${args[0]} and ${args[1]} arguments`
  287. break
  288. default:
  289. {
  290. const last = args.pop()
  291. msg += `The ${args.join(', ')}, and ${last} arguments`
  292. }
  293. break
  294. }
  295. return `${msg} must be specified`
  296. },
  297. TypeError
  298. )
  299. E(
  300. 'ERR_OUT_OF_RANGE',
  301. (str, range, input) => {
  302. assert(range, 'Missing "range" argument')
  303. let received
  304. if (Number.isInteger(input) && Math.abs(input) > 2 ** 32) {
  305. received = addNumericalSeparator(String(input))
  306. } else if (typeof input === 'bigint') {
  307. received = String(input)
  308. const limit = BigInt(2) ** BigInt(32)
  309. if (input > limit || input < -limit) {
  310. received = addNumericalSeparator(received)
  311. }
  312. received += 'n'
  313. } else {
  314. received = inspect(input)
  315. }
  316. return `The value of "${str}" is out of range. It must be ${range}. Received ${received}`
  317. },
  318. RangeError
  319. )
  320. E('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times', Error)
  321. E('ERR_METHOD_NOT_IMPLEMENTED', 'The %s method is not implemented', Error)
  322. E('ERR_STREAM_ALREADY_FINISHED', 'Cannot call %s after a stream was finished', Error)
  323. E('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable', Error)
  324. E('ERR_STREAM_DESTROYED', 'Cannot call %s after a stream was destroyed', Error)
  325. E('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError)
  326. E('ERR_STREAM_PREMATURE_CLOSE', 'Premature close', Error)
  327. E('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF', Error)
  328. E('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', 'stream.unshift() after end event', Error)
  329. E('ERR_STREAM_WRITE_AFTER_END', 'write after end', Error)
  330. E('ERR_UNKNOWN_ENCODING', 'Unknown encoding: %s', TypeError)
  331. module.exports = {
  332. AbortError,
  333. aggregateTwoErrors: hideStackFrames(aggregateTwoErrors),
  334. hideStackFrames,
  335. codes
  336. }