Merge an error with its inner cause
npm install merge-error-cause






Merge an error with its cause.
This mergeserror.cause
recursively with its parent error, including itsmessage,stack,name
anderrors.
``js
import mergeErrorCause from 'merge-error-cause'
const main = (userId) => {
try {
return createUser(userId)
} catch (error) {
throw mergeErrorCause(error)
// Printed as:
// TypeError: Invalid user id: false
// Could not create user.
}
}
const createUser = (userId) => {
try {
validateUserId(userId)
return sendDatabaseRequest('create', userId)
} catch (cause) {
throw new Error('Could not create user.', { cause })
}
}
const validateUserId = (userId) => {
if (typeof userId !== 'string') {
throw new TypeError(Invalid user id: ${userId}.)
}
}
main(false)
`
`bash`
npm install merge-error-cause
This package works in both Node.js >=18.18.0 and
browsers.
This is an ES module. It must be loaded using
an import or import() statement,
not require(). If TypeScript is used, it must be configured to
output ES modules,
not CommonJS.
error Error | any\Error
_Return value_:
error is modified and returned.
If error's class is Error or if error.wrap is true,error.cause
is modified and returned instead.
If error is not a valid Error, a new error is created and returned
instead.
This never throws.
error.cause
is a
recent
JavaScript feature to wrap error messages and properties.
`js`
try {
validateUserId(userId)
sendDatabaseRequest('create', userId)
} catch (cause) {
throw new Error('Could not create user.', { cause })
}
However, it comes with a few issues.
Consumers need to traverse error.cause.
`jserror.cause
try {
createUser(userId)
} catch (error) {
if (error.code === 'E101' || (error.cause && error.cause.code === 'E101')) {
// Checking for properties requires traversing
}
if (
error.name === 'UserError' ||
(error.cause && error.cause.name === 'UserError')
) {
// So does checking for error class
}
}
`
This is tricky to get right. For example:
- error.cause.cause might also exist (and so on)error
- If is not an Error instance, error.name might throwerror.cause
- Recursing over might be an infinite cycle
This library merges error.cause recursively. It alsoerror
ensures is an Error instance. Consumers can thencause
handle errors without checking its .
`js
try {
createUser(userId)
} catch (error) {
if (error.code === 'E101') {
/ ... /
}
if (error.name === 'UserError') {
/ ... /
}
}
`
Stack traces with multiple error.cause can be quite verbose.
``
Error: Could not create user group.
at createUserGroup (/home/user/app/user_group.js:19:9)
at createGroups (/home/user/app/user_group.js:101:10)
at startApp (/home/user/app/app.js:35:20)
at main (/home/user/app/app.js:3:4) {
[cause]: Error: Could not create user.
at newUser (/home/user/app/user.js:52:7)
at createUser (/home/user/app/user.js:43:5)
at createUserGroup (/home/user/app/user_group.js:17:11)
at createGroups (/home/user/app/user_group.js:101:10)
at startApp (/home/user/app/app.js:35:20)
at main (/home/user/app/app.js:3:4) {
[cause]: Error: Invalid user.
at validateUser (/home/user/app/user.js:159:8)
at userInstance (/home/user/app/user.js:20:4)
at newUser (/home/user/app/user.js:50:7)
at createUser (/home/user/app/user.js:43:5)
at createUserGroup (/home/user/app/user_group.js:17:11)
at createGroups (/home/user/app/user_group.js:101:10)
at startApp (/home/user/app/app.js:35:20)
at main (/home/user/app/app.js:3:4) {
[cause]: UserError: User "15" does not exist.
at checkUserId (/home/user/app/user.js:195:3)
at checkUserExist (/home/user/app/user.js:170:10)
at validateUser (/home/user/app/user.js:157:23)
at userInstance (/home/user/app/user.js:20:4)
at newUser (/home/user/app/user.js:50:7)
at createUser (/home/user/app/user.js:43:5)
at createUserGroup (/home/user/app/user_group.js:17:11)
at createGroups (/home/user/app/user_group.js:101:10)
at startApp (/home/user/app/app.js:35:20)
at main (/home/user/app/app.js:3:4)
}
}
}
Each error cause is indented and printed separately.
- The stack traces mostly repeat each other since the function calls are part of
the same line execution
- The most relevant message (innermost) is harder to find since it is shown last
This library only keeps the innermost stack trace. Error messages are
concatenated by default from innermost to outermost. This results in much
simpler stack traces without losing any information.
``
TypeError: User "15" does not exist.
Invalid user.
Could not create user.
Could not create user group.
at checkUserId (/home/user/app/user.js:195:3)
at checkUserExist (/home/user/app/user.js:170:10)
at validateUser (/home/user/app/user.js:157:23)
at userInstance (/home/user/app/user.js:20:4)
at newUser (/home/user/app/user.js:50:7)
at createUser (/home/user/app/user.js:43:5)
at createUserGroup (/home/user/app/user_group.js:17:11)
at createGroups (/home/user/app/user_group.js:101:10)
at startApp (/home/user/app/app.js:35:20)
at main (/home/user/app/app.js:3:4)
Only the innermost stack trace is kept.
Please make sure you use async/await instead of new Promise() or callbacks
to prevent truncated stack traces.
Inner error messages are printed first.
`js`
try {
throw new Error('Invalid user id.')
} catch (cause) {
throw new Error('Could not create user.', { cause })
// Printed as:
// Error: Invalid user id.
// Could not create user.
}
If the outer error message ends with :, it is prepended instead.
`js`
try {
throw new Error('Invalid user id.')
} catch (cause) {
throw new Error('Could not create user:', { cause })
// Printed as:
// Error: Could not create user: Invalid user id.
}
: can optionally be followed by a newline.
`js`
try {
throw new Error('Invalid user id.')
} catch (cause) {
throw new Error('Could not create user:\n', { cause })
// Printed as:
// Error: Could not create user:
// Invalid user id.
}
The outer error class is used.
`js`
try {
throw new TypeError('User id is not a string.')
} catch (cause) {
const error = new UserError('Could not create user.', { cause })
const mergedError = mergeErrorCause(error)
console.log(mergedError instanceof UserError) // true
console.log(mergedError.name) // 'UserError'
}
If the parent error class is Error, the child class is used instead. This
allows wrapping the error message or properties while keeping its class.
`js`
try {
throw new TypeError('User id is not a string.')
} catch (cause) {
const error = new Error('Could not create user.', { cause })
console.log(mergeErrorCause(error) instanceof TypeError) // true
}
error.wrap: true has the same effect, but works with any parent error class.
`js`
try {
throw new TypeError('User id is not a string.')
} catch (cause) {
const error = new UserError('Could not create user.', { cause })
error.wrap = true
console.log(mergeErrorCause(error) instanceof TypeError) // true
}
Error properties are shallowly merged.
`jsuserId
// Both and invalidUser are kept`
try {
throw Object.assign(new Error('Invalid user id.'), { userId: '5' })
} catch (cause) {
throw Object.assign(new Error('Could not create user.', { cause }), {
invalidUser: true,
})
}
Empty error messages are ignored. This is useful when wrapping error properties.
`js`
try {
throw new Error('Invalid user id.')
} catch (cause) {
throw Object.assign(new Error('', { cause }), { invalidUser: true })
}
Any
[aggregateError.errors[*].cause](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError)aggregateError.errors
is processed recursively. However, are not merged with
each other since those are different from each other.
If both error.errors and error.cause.errors exist, they are concatenated.
Invalid errors are normalized
to proper Error instances.
`js`
try {
throw 'Invalid user id.'
} catch (error) {
console.log(mergeErrorCause(error)) // Error: Invalid user id.
}
- modern-errors: Handle errors in
a simple, stable, consistent way
- error-custom-class: Create
one error class
- error-class-utils: Utilities
to properly create error classes
- error-serializer: Convert
errors to/from plain objects
- normalize-exception:
Normalize exceptions/errors
- is-error-instance: Check if
a value is an Error instanceset-error-class
- : Properlyset-error-message
update an error's class
- : Properlywrap-error-message
update an error's message
- :set-error-props
Properly wrap an error's message
- : Properlyset-error-stack
update an error's properties
- : Properlyerror-cause-polyfill
update an error's stack
- :error.cause
Polyfill handle-cli-error
- : 💣 Errorlog-process-errors
handler for CLI applications 💥
- : Showerror-http-response
some ❤ to Node.js process errors
- :winston-error-format
Create HTTP error responses
- : Log
errors with Winston
For any question, _don't hesitate_ to submit an issue on GitHub.
Everyone is welcome regardless of personal background. We enforce a
Code of conduct in order to promote a positive and
inclusive environment.
This project was made with ❤️. The simplest way to give back is by starring and
sharing it online.
If the documentation is unclear or has a typo, please click on the page's Edit`
button (pencil icon) and suggest a correction.
If you would like to help us fix a bug or add a new feature, please check our
guidelines. Pull requests are welcome!