Skip to main content

How to extend Error with custom properties?

1. The main idea

You create your own class that inherits from Error, and add the properties you need to it - for example: code, status, details, cause, context, and so on.

2. Example: a basic custom error

javascript
class AppError extends Error { constructor(message, code) { super(message); // pass the message to the parent (Error) this.name = "AppError"; // set the error's name this.code = code; // add our own property } } try { throw new AppError("Something went wrong", "SERVER_ERROR"); } catch (e) { console.log(e.name); // AppError console.log(e.message); // Something went wrong console.log(e.code); // SERVER_ERROR console.log(e.stack); // Call stack }

Now your error has both the standard properties (message, stack) and custom ones (code).

3. Example: adding an HTTP code and context

This is convenient for APIs, especially in NestJS / Express:

javascript
class HttpError extends Error { constructor(message, status = 500, details = {}) { super(message); this.name = this.constructor.name; this.status = status; this.details = details; if (Error.captureStackTrace) { Error.captureStackTrace(this, this.constructor); } } } throw new HttpError("User not found", 404, { userId: 123 });

When handling the error:

javascript
try { throw new HttpError("User not found", 404, { userId: 123 }); } catch (err) { console.log(err.name); // HttpError console.log(err.status); // 404 console.log(err.details); // { userId: 123 } }

4. Example: a hierarchy of custom errors

You can build a whole "tree" of errors:

javascript
class AppError extends Error { constructor(message, code) { super(message); this.name = this.constructor.name; this.code = code; Error.captureStackTrace?.(this, this.constructor); } } class ValidationError extends AppError { constructor(message, field) { super(message, "VALIDATION_ERROR"); this.field = field; } } class AuthError extends AppError { constructor(message = "Unauthorized") { super(message, "AUTH_ERROR"); this.status = 401; } }

Usage:

javascript
try { throw new ValidationError("Invalid email", "email"); } catch (err) { if (err instanceof ValidationError) { console.log("Error in field:", err.field); } console.log(err.code); // VALIDATION_ERROR }

5. Example with cause (ES2022+)

The modern ECMAScript standard supports the cause property, to store the original error that led to the current one.

javascript
try { JSON.parse("invalid JSON"); } catch (parseErr) { throw new AppError("Error parsing JSON", "PARSE_ERROR", { cause: parseErr }); }

To support cause, you need to add it to the constructor:

javascript
class AppError extends Error { constructor(message, code, options = {}) { super(message, options); this.name = this.constructor.name; this.code = code; this.cause = options.cause; } }

6. Example of usage in production (Express / NestJS)

javascript
app.use((err, req, res, next) => { if (err instanceof AppError) { res.status(err.status || 500).json({ error: err.name, message: err.message, code: err.code, details: err.details, }); } else { res.status(500).json({ error: "InternalError", message: "Unknown error" }); } });

7. Why extend Error

ReasonExample
Clear, understandable messages"Email is required" instead of "Unexpected token"
Structure and typingValidationError, AuthError, HttpError
Convenient loggingYou can include code, status, details
Storing the original causevia cause
Easier debuggingDifferent error types are easy to tell apart in catch

Summary

To extend Error with your own properties:

javascript
class MyError extends Error { constructor(message, customProp) { super(message); this.name = "MyError"; this.customProp = customProp; Error.captureStackTrace?.(this, this.constructor); } }

And use it:

javascript
throw new MyError("Database error", { query: "SELECT * FROM users" });

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.