How do you create a custom Error class?
By FrontendPro Editorial Team Updated 9/26/2026
Answer
Extend the built-in Error and set a name. Extending matters: it gives you a stack trace, makes instanceof work, and lets the error behave like any other in logs and debuggers.
class ValidationError extends Error {
constructor(field, message) {
super(message);
this.name = "ValidationError";
this.field = field;
}
}
throw new ValidationError("email", "Email is not valid");
Always call super(message) first — message and the stack are set by the parent constructor, and touching this before it throws.
Why bother with a class at all
Custom types let callers handle different failures differently, without parsing message strings:
try {
await saveUser(input);
} catch (err) {
if (err instanceof ValidationError) return showFieldError(err.field);
if (err instanceof NetworkError) return retry();
throw err; // not ours — let it propagate
}
Rethrowing what you do not recognise is the part people skip, and it is what turns a helpful error into a silent one.
Preserving the original cause
When you wrap a lower-level failure, pass it as cause instead of discarding it:
try {
await db.query(sql);
} catch (err) {
throw new Error("Could not load user", { cause: err });
}
The original stack stays reachable through error.cause, which is the difference between a debuggable error and a dead end.
Two things to know
error.name is what appears in the console prefix, so setting it is worth the one line. And always throw Error objects rather than strings or plain objects — only real errors carry a stack, and throw "oops" produces a value that most logging and monitoring tools cannot make sense of.
