How can you create custom error objects?
TL;DR
Extend Error when callers need to distinguish a domain failure from other exceptions or inspect structured context. Call super(message, options), give the class a useful name, and add stable properties such as a machine-readable code. Preserve an underlying error with the standard cause option instead of replacing its diagnostic context.
class ValidationError extends Error {constructor(message, { field, cause } = {}) {super(message, { cause });this.name = 'ValidationError';this.code = 'INVALID_INPUT';this.field = field;}}try {throw new ValidationError('Email is invalid', { field: 'email' });} catch (error) {if (error instanceof ValidationError) {console.log(error.field); // email} else {throw error;}}
How can you create custom error objects?
Extending the Error class
To create a custom error object, you can extend the built-in Error class. This allows you to inherit the properties and methods of the Error class while adding your own custom properties and methods.
class CustomError extends Error {constructor(message) {super(message);this.name = 'CustomError';}}
Adding custom properties
You can add custom properties to your custom error class to provide more context about the error.
class CustomError extends Error {constructor(message, errorCode) {super(message);this.name = 'CustomError';this.errorCode = errorCode;}}try {throw new CustomError('This is a custom error message', 404);} catch (error) {console.log(error.name); // CustomErrorconsole.log(error.message); // This is a custom error messageconsole.log(error.errorCode); // 404}
Custom methods
You can also add custom methods to your custom error class to handle specific error-related logic.
class CustomError extends Error {constructor(message, errorCode) {super(message);this.name = 'CustomError';this.errorCode = errorCode;}logError() {console.error(`${this.name} [${this.errorCode}]: ${this.message}`);}}try {throw new CustomError('This is a custom error message', 404);} catch (error) {error.logError(); // CustomError [404]: This is a custom error message}
Using instanceof to check for custom errors
You can use the instanceof operator to check if an error is an instance of your custom error class.
class CustomError extends Error {constructor(message, errorCode) {super(message);this.name = 'CustomError';this.errorCode = errorCode;}}try {throw new CustomError('This is a custom error message', 404);} catch (error) {if (error instanceof CustomError) {console.log('Caught a CustomError');} else {console.log('Caught a different type of error');}}
Catch a custom error only where you can recover, translate it into an expected API result, or add useful context. Re-throw unexpected errors so programming defects are not silently treated as validation failures.
Preserving the original cause
Wrapping a low-level error can give callers a domain-specific error without losing the original failure:
class UserLookupError extends Error {constructor(userId, options) {super(`Could not load user ${userId}`, options);this.name = 'UserLookupError';this.userId = userId;}}async function loadUser(userId) {try {return await database.findUser(userId);} catch (cause) {throw new UserLookupError(userId, { cause });}}
Error stacks, causes, and custom properties may contain secrets or personal data. Log them only in trusted systems with suitable redaction, and send a deliberately shaped public error response rather than serializing the error object directly.