Instantly share code, notes, and snippets.

Embed
What would you like to do?
Creating custom Error classes in Node.js
'use strict';
module.exports = function CustomError(message, extra) {
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
this.message = message;
this.extra = extra;
};
require('util').inherits(module.exports, Error);

It's nice to be able to distinguish error types by classes. But it's a bit tricky to correctly create a custom error class in Node.js, so here is an example.

The example also shows how to add an extra parameter called extra that will be stored as a property on the error.

Usage

var CustomError = require('./errors/custom-error');

function doSomethingBad() {
  throw new CustomError('It went bad!', 42);
}

Features

  • Name appears once - less editing if you have to create lots of custom error classes
  • Easy to subclass - just change the last line to inherit from another custom error class you created
  • Correct stack trace - no extra stack frames, no double capturing of the stack trace

Anti-patterns

These are some things that I've seen in other proposed solutions that you should avoid.

  • Error.call(this) - creates another error object (wasting a bunch of time) and doesn't touch this at all
  • Error.captureStackTrace(this, arguments.callee); - works, but arguments.callee is deprecated, so don't use it
  • this.stack = (new Error).stack - this... I don't even...
// Mini test suite for our custom error
var assert = require('assert');
var CustomError = require('./errors/custom-error');
function doSomethingBad() {
throw new CustomError('It went bad!', 42);
}
try {
doSomethingBad();
} catch (err) {
// The name property should be set to the error's name
assert(err.name = 'CustomError');
// The error should be an instance of its class
assert(err instanceof CustomError);
// The error should be an instance of builtin Error
assert(err instanceof Error);
// The error should be recognized by Node.js' util#isError
assert(require('util').isError(err));
// The error should have recorded a stack
assert(err.stack);
// toString should return the default error message formatting
assert.strictEqual(err.toString(),
'CustomError: It went bad!');
// The stack should start with the default error message formatting
assert.strictEqual(err.stack.split('\n')[0],
'CustomError: It went bad!');
// The first stack frame should be the function where the error was thrown.
assert.strictEqual(err.stack.split('\n')[1].indexOf('doSomethingBad'), 7);
// The extra property should have been set
assert.strictEqual(err.extra, 42);
}
// Spoiler: It passes!
@Venemo

This comment has been minimized.

Venemo commented Jun 8, 2015

In the test suite, there is a small typo: assert(err.name = 'CustomError'); should be assert(err.name === 'CustomError'); or assert.strictEqual(err.name, 'CustomError');

@tallesl

This comment has been minimized.

tallesl commented Jul 10, 2015

Error.captureStackTrace is V8 exclusive, if you want to be extra careful call it this way:

Error.captureStackTrace && Error.captureStackTrace(this, this.constructor);

(great gist by the way!)

@shamasis

This comment has been minimized.

shamasis commented Aug 24, 2015

👍

@bgSosh

This comment has been minimized.

bgSosh commented Oct 8, 2015

This is excellent, thank you.

I'd previously been following suggestions from MDN, but was not really satisfied.

@hassansin

This comment has been minimized.

hassansin commented Dec 21, 2015

How do you match the string representation of CustomError to that of Error object?

Object.prototype.toString.call(new Error); // '[object Error]'
Object.prototype.toString.call(new CustomError); //  '[object Object]'
@thefourtheye

This comment has been minimized.

thefourtheye commented Jan 25, 2016

@hassansin in ES5, you cannot change the toString representation of an Object.

@netmikey

This comment has been minimized.

netmikey commented Jan 27, 2016

I'm using Visual Studio Code and am getting a Warning when creating the Error with new:

Only a void function can be called with the 'new' keyword.

Any idea whether there's an easy way to fix that?

@parallaxisjones

This comment has been minimized.

parallaxisjones commented Mar 18, 2016

@netmikey are you returning something from the constructor? If you are you shouldn't have to and it sounds like that's what the warning means.

By default an object called with the new keyword returns 'this'

@jsmrcaga

This comment has been minimized.

jsmrcaga commented Apr 1, 2016

👍

@chrismbeckett

This comment has been minimized.

chrismbeckett commented May 7, 2016

And here is a working snippet for use with ES2015 classes:

class CustomError extends Error {
  constructor ( message, extra ) {
    super()
    Error.captureStackTrace( this, this.constructor )
    this.name = 'CustomError'
    this.message = message
    if ( extra ) this.extra = extra
  }
}

FYI - be careful using this.constructor.name - if you minify/obfuscate your code for any reason, the name gets mangled.

@calidion

This comment has been minimized.

calidion commented Jul 10, 2016

Thanks for the code snippet.
I have updated my project errorable with this method.

https://github.com/calidion/errorable

Errorable is a simple tool to define errors and can be easily adopted for APIs.
Hope it helps.

@westy92

This comment has been minimized.

westy92 commented Jul 30, 2016

I'm using TypeScript and came up with the following solution:

class MyError extends Error {

  static name: string;

  constructor(public message?: string, public extra?: number) {
    super(message);
    Error.captureStackTrace(this, MyError);
    this.name = (this as any).constructor.name; // OR this.name = (<any>this).constructor.name;
  }

};

Note that you must have the node typings installed in order to use Error.captureStackTrace.

@slavafomin

This comment has been minimized.

slavafomin commented Sep 3, 2016

Thank you for this great Gist!

I've made a custom version of it to be used in ES6/ES2015:
https://gist.github.com/slavafomin/b164e3e710a6fc9352c934b9073e7216

Would love to hear your opinion on it.

@mizunashi-mana

This comment has been minimized.

mizunashi-mana commented Nov 23, 2016

In Node 7.1 and TypeScript, this works:

export class ErrorBase extends Error {
  public name: string;

  constructor(message: string) {
    super(message);
    this.name = (this.constructor as any).name;
    this.message = message;
    Error.captureStackTrace(this, this.constructor); // after initialize properties
  }
}

Updated:

this features was added in typescript 2.1, so this implementation is better (see also below comment):

export interface ErrorBase extends Error {
  readonly name: string;
  readonly message: string;
  readonly stack: string;
};
export interface ErrorBaseConstructor {
  new (message: string): ErrorBase;
  readonly prototype: ErrorBase;
}

export const ErrorBase: ErrorBaseConstructor = <any>class ErrorBase {
  public constructor(message: string) {
    Object.defineProperty(this, 'name', {
      get: () => (this.constructor as any).name,
    });
    Object.defineProperty(this, 'message', {
      get: () => message,
    });
    Error.captureStackTrace(this, this.constructor);
  }
};
(ErrorBase as any).prototype = Object.create(Error.prototype);
ErrorBase.prototype.constructor = ErrorBase;
@urugator

This comment has been minimized.

urugator commented Nov 23, 2016

In Node (7.2) console.log will print custom errors a bit differently unless all properties are defined as non-enumerable:

class CustomError extends Error {
  constructor(...args) {    
    super(...args);    
    Object.defineProperty(this, "name", {           
      value: this.constructor.name
    });        
    Error.captureStackTrace(this, this.constructor);
  }
}
@mathieug

This comment has been minimized.

mathieug commented Jan 20, 2017

@urugator: do you have a link about this? Thank you!

@gunar

This comment has been minimized.

gunar commented Apr 11, 2017

I avoid classes (classical-inheritance is flawed) and types (google: instanceof lies).
The best solution is just to duck-type. See http://gunargessner.com/subclassing-exception/.

@tinybug

This comment has been minimized.

tinybug commented Jul 10, 2017

It's more better move Error.captureStackTrace(this, this.constructor); after this.message = message;, then the e.stack will output like this:
CustomError: custom ...
Otherwise it will output:
Error ...
that output of stack will miss error type and message

@mrenrich84

This comment has been minimized.

mrenrich84 commented Aug 12, 2017

So tricky

node --version  
v7.10.0
module.exports = function CustomError(message, extra) {
  this.name = this.constructor.name
  this.message = message
  Error.captureStackTrace(this, this.constructor)
  this.extra = extra
}

module.exports.prototype.inspect = function () {
  return this.stack
}

Error.test.js

class MyCustomError extends require('./Errors') {}

try {
  throw new MyCustomError ('message')
} catch (err) {
  console.log(err);
  console.log(err.constructor.name);
  console.log(err instanceof MyCustomError)
  console.log(err.stack);
}

node Error.test.js

MyCustomError: message
    at Object.<anonymous> (Error.test.js:4:9)
    at Module._compile (module.js:571:32)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.runMain (module.js:605:10)
    at run (bootstrap_node.js:427:7)
    at startup (bootstrap_node.js:151:9)
    at bootstrap_node.js:542:3

https://gist.github.com/mrenrich84/f499c4697a5f3e650f37def3ec8acc22

@shivarajnaidu

This comment has been minimized.

shivarajnaidu commented Feb 21, 2018

Why Not

     function MyCustomError(message, extra) {
          const error  = new Error(message);
          error.name = 'MyCustomError';
          if (extra) {
                error.extra = extra;
           }

          return error;
     }


throw MyCustomError('Something Went Wrong');
@constgen

This comment has been minimized.

constgen commented Mar 2, 2018

Because

var error = new MyCustomError('Something Went Wrong');
error instanceof MyCustomError // false

Which is not expected. You can't check the type of error for example in this case

try { }
catch(err){
  switch(true){
    case err instanceof MyCustomError: break;
    case err instanceof Error: break;
  }
}
@ramzes13

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment