Skip to content

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!
@tinybug
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

ramzes13 commented Oct 26, 2018

@laggingreflex
Copy link

laggingreflex commented Dec 21, 2018

These were all showing up in the console as an object, not as the usual Error:

{ CustomError: test
    at ... 
        name: 'CustomError' }

Instead if I use the name getter, I get the usual error-like appearance:

class CustomError { 
    get name () { return this.constructor.name } 
}
CustomError: test
    at ...

@tcf909
Copy link

tcf909 commented Jul 15, 2019

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]'

You need to define the toStringTag for the CustomError:

Object.defineProperty(CustomError.prototype, Symbol.toStringTag, {
	value: 'Error',
	writable: false, configurable: false, enumerable: false
});

This consequently help with libraries like lodash and it's _.isError().

@hiddentao
Copy link

hiddentao commented Jun 27, 2022

Thanks for the gist, and thanks to all the commenters for the suggestions.

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