
Product
Introducing Socket Firewall Enterprise: Flexible, Configurable Protection for Modern Package Ecosystems
Socket Firewall Enterprise is now available with flexible deployment, configurable policies, and expanded language support.
modern-errors
Advanced tools
Handle errors like it's 2022 🔮
Error handling framework that is minimalist yet featureful.
Error instance, missing
stack, etc.)Create custom error types.
// `error.js`
import modernErrors from 'modern-errors'
export const { InputError, AuthError, DatabaseError, errorHandler, parse } =
modernErrors(['InputError', 'AuthError', 'DatabaseError'])
Wrap the main function with the error handler.
import { errorHandler } from './error.js'
export const main = async function (filePath) {
try {
return await readContents(filePath)
} catch (error) {
throw errorHandler(error)
}
}
Throw/re-throw errors.
import { InputError } from './error.js'
const readContents = async function (filePath) {
try {
return await readFile(filePath)
} catch (cause) {
throw new InputError(`Could not read ${filePath}`, { cause })
}
}
npm install modern-errors
This package is an ES module and must be loaded using
an import or import() statement,
not require().
errorNames string[]
options object
Return value: object
Creates custom error types.
Type: CustomErrorType
Any error name passed as argument is returned as an error type.
Type: (anyException) => Error
Error handler that should wrap each main function.
Type: (errorObject) => Error
Convert an error plain object into an Error instance.
Type: string | URL
URL where users should report unknown errors.
Type: (error, parameters) => void
Called on any
new CustomErrorType('message', parameters). Can be
used to customize error parameters or set
error type properties. By default, any parameters
are set as error properties.
// error.js
import modernErrors from 'modern-errors'
export const { InputError, AuthError, DatabaseError, errorHandler, parse } =
modernErrors(['InputError', 'AuthError', 'DatabaseError'])
Each main function should be wrapped with the errorHandler().
import { errorHandler } from './error.js'
export const main = async function (filePath) {
try {
return await readContents(filePath)
} catch (error) {
// `errorHandler()` returns `error`, so `throw` must be used
throw errorHandler(error)
}
}
import { InputError } from './error.js'
const validateFilePath = function (filePath) {
if (filePath === '') {
throw new InputError('Missing file path.')
}
}
Invalid errors are normalized
by errorHandler(). This includes errors that are not an
Error instance
or that have
wrong/missing properties.
import { errorHandler } from './error.js'
export const main = function (filePath) {
try {
throw 'Missing file path.'
} catch (error) {
throw errorHandler(error) // Normalized to an `Error` instance
}
}
Errors are re-thrown using the
standard cause parameter.
This allows wrapping the error message,
properties, or type.
import { InputError } from './error.js'
const readContents = async function (filePath) {
try {
return await readFile(filePath)
} catch (cause) {
throw new InputError(`Could not read ${filePath}`, { cause })
}
}
The errorHandler()
merges all error cause into a
single error, including their
message,
stack,
name,
AggregateError.errors
and any additional property. This ensures:
error.cause does not need to be
traversedThe outer error message is appended.
try {
await readFile(filePath)
} catch (cause) {
throw new InputError(`Could not read ${filePath}`, { cause })
// InputError: File does not exist.
// Could not read /example/path
}
If the outer error message ends with :, it is prepended instead.
throw new InputError(`Could not read ${filePath}:`, { cause })
// InputError: Could not read /example/path: File does not exist.
: can optionally be followed a newline.
throw new InputError(`Could not read ${filePath}:\n`, { cause })
// InputError: Could not read /example/path:
// File does not exist.
Once errorHandler() has been applied, the error type can be
checked by its name. Libraries should document their possible error names, but
do not need to export their error types.
if (error.name === 'InputError') {
// ...
} else if (error.name === 'UnknownError') {
// ...
}
When re-throwing errors, the outer error type overrides the inner one.
try {
throw new AuthError('Could not authenticate.')
} catch (cause) {
throw new InputError('Could not read the file.', { cause })
// Now an InputError
}
However, the inner error type is kept if the outer one is Error or
AggregateError.
try {
throw new AuthError('Could not authenticate.')
} catch (cause) {
throw new Error('Could not read the file.', { cause })
// Still an AuthError
}
All errors should use known types: the ones returned by
modernErrors(). Errors with an unknown type
should be handled in try {} catch {} and re-thrown with a
known type instead.
The errorHandler() assigns the UnknownError type to any
error with an unknown type.
const getUserId = function (user) {
return user.id
}
getUserId(null) // UnknownError: Cannot read properties of null (reading 'id')
If the bugsUrl option is used,
modernErrors({ bugsUrl: 'https://github.com/my-name/my-project/issues' })
any unknown error will include the following message.
Please report this bug at: https://github.com/my-name/my-project/issues
Unless the onCreate() option is defined, any parameter is set as
an error property.
const error = new InputError('Could not read the file.', { filePath: '/path' })
console.log(error.filePath) // '/path'
Pass an empty message in order to set error properties without wrapping the
message.
try {
await readFile(filePath)
} catch (cause) {
throw new Error('', { cause, filePath: '/path' })
}
The onCreate() option can be used to validate and transform error
parameters.
modernErrors({
onCreate(error, parameters) {
const { filePath } = parameters
if (typeof filePath !== 'string') {
throw new Error('filePath must be a string.')
}
const hasFilePath = filePath !== undefined
Object.assign(error, { filePath, hasFilePath })
},
})
const error = new InputError('Could not read the file.', {
filePath: '/path',
unknownParam: true,
})
console.log(error.filePath) // '/path'
console.log(error.hasFilePath) // true
console.log(error.unknownParam) // undefined
The onCreate() option can trigger error type-specific logic.
modernErrors({
onCreate(error, parameters) {
onCreateError[error.name](error, parameters)
},
})
const onCreateError = {
InputError(error, parameters) {
// ...
},
AuthError(error, parameters) {
// ...
},
// ...
}
The onCreate() option can be used to set properties on all
instances of a given error type.
modernErrors({
onCreate(error, parameters) {
Object.assign(error, parameters, ERROR_PROPS[error.name])
},
})
const ERROR_PROPS = {
InputError: { isUser: true },
AuthError: { isUser: true },
DatabaseError: { isUser: false },
}
const error = new InputError('Could not read the file.')
console.log(error.isUser) // true
CLI applications can assign a different exit code and log verbosity per error
type by using handle-cli-error.
#!/usr/bin/env node
import handleCliError from 'handle-cli-error'
// `programmaticMain()` must use `modern-errors`'s `errorHandler`
import programmaticMain from './main.js'
const cliMain = function () {
try {
const cliFlags = getCliFlags()
programmaticMain(cliFlags)
} catch (error) {
// Print `error` then exit the process
handleCliError(error, {
types: {
InputError: { exitCode: 1, short: true },
DatabaseError: { exitCode: 2, short: true },
default: { exitCode: 3 },
},
})
}
}
cliMain()
error.toJSON() converts errors to plain objects that are
serializable to JSON
(or YAML,
etc.). It is
automatically called
by JSON.stringify(). All error properties
are kept,
including
cause.
The error must be from a known type. However, any other
error (including Error, TypeError, RangeError, etc.) is also serializable
providing it has been either passed to errorHandler(), or
wrapped as an error.cause.
try {
await readFile(filePath)
} catch (cause) {
const error = new InputError('Could not read the file.', {
cause,
filePath: '/path',
})
const errorObject = error.toJSON()
// {
// name: 'InputError',
// message: 'Could not read the file',
// stack: '...',
// cause: { name: 'Error', ... },
// filePath: '/path'
// }
const errorString = JSON.stringify(error)
// '{"name":"InputError",...}'
}
parse(errorObject) converts those error plain objects back to
identical error instances.
The original error type is generically preserved. However, it is converted to a
generic Error if it is neither a native type (TypeError, RangeError, etc.)
nor a known type.
const newErrorObject = JSON.parse(errorString)
const newError = parse(newErrorObject)
// InputError: Could not read the file.
// filePath: '/path'
// [cause]: Error: ...
Objects and arrays containing custom errors can be deeply serialized to JSON.
They can then be deeply parsed back using
JSON.parse()'s reviver.
const error = new InputError('Could not read the file.')
const deepObject = [{}, { error }]
const jsonString = JSON.stringify(deepObject)
const newDeepObject = JSON.parse(jsonString, (key, value) => parse(value))
console.log(newDeepObject[1].error) // InputError: Could not read the file.
This framework brings together a collection of modules which can also be used individually:
create-error-types: Create
multiple error typeserror-type: Create one error typeerror-serializer: Convert
errors to/from plain objectsnormalize-exception:
Normalize exceptions/errorsmerge-error-cause: Merge an
error with its causeerror-cause-polyfill:
Polyfill error.causehandle-cli-error: 💣 Error
handler for CLI applications 💥log-process-errors: Show
some ❤ to Node.js process errorsFor 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!
FAQs
Handle errors in a simple, stable, consistent way
The npm package modern-errors receives a total of 4,026 weekly downloads. As such, modern-errors popularity was classified as popular.
We found that modern-errors demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Socket Firewall Enterprise is now available with flexible deployment, configurable policies, and expanded language support.

Security News
Open source dashboard CNAPulse tracks CVE Numbering Authorities’ publishing activity, highlighting trends and transparency across the CVE ecosystem.

Product
Detect malware, unsafe data flows, and license issues in GitHub Actions with Socket’s new workflow scanning support.