tl;dr You can stringify an Error in JavaScript with JSON.stringify(err, Object.getOwnPropertyNames(err)).

You’re writing some error handling in JavaScript and you decide to dump the whole error to a log, stringified using JSON.stringify. Smart move, having the full error logged can prove to be invaluable. Time goes on, you experience an outage one evening. Not to worry, you think, I’ll just check out the logs and diagnose the issue. You open the logs are are greeted with a series of {} where you expect to see stringified Errors. Wat.

In your confusion, you open a node terminal and type

var err = new Error('testing error because lol it is not logging right')
console.log(JSON.stringify(err))

Much to your dismay, the output is in fact {}.

Wat?

Wat?

Let’s unpack this.

Object.getOwnPropertyDescriptor

Object.getOwnPropertyDescriptor is a function in that gives us some information about a given property in JavaScript. Among other things, this function will tell us if a given property is enumerable. If a given property is enumerable, it will show up when you enumerate an object’s properties. For example, you can enumerate, or list, an object’s properties by using the Object.keys function.

If you do this

var test = { foo: 'bar' }

followed by this

Object.getOwnPropertyDescriptor(test, 'foo')

you will get this

{ value: 'bar',
  writable: true,
  enumerable: true,
  configurable: true }

You’ll notice that the output of Object.getOwnPropertyDescriptor(test, 'foo') contains enumerable: true. Becuase that is true, our property foo will show up when we enumerate our object’s properties with Object.keys(test).

If, for some reason, we did not want our property foo to show up when we enumerate our object’s properties, we can use Object.defineProperty.

Object.defineProperty

Object.defineProperty allows us to create a property on a given object. It also allows us to control the descriptor for that property, or the metadata about that property. One of the pieces of metadata that we can control is whether or not a given property is enumerable.

If you do this

var test = {}

followed by this

Object.defineProperty(test, 'foo', {enumerable: false, value: 'bar'})

you will get this

{}

also this

Object.keys(test)

will give you this

{}

By setting enumerable to false for our foo property, we prevent it from showing up when our object’s properties are enumerated.

Stringify Errors

To be honest, I’ve never had to use the functions Object.defineProperty or Object.getOwnPropertyDescriptor until I tried to stringify an Error. Frankly, I think it is silly that the important bits on Error are not marked as enumerable.

If you do this

var err = new Error('lol, error testing')

followed by this

Object.getOwnPropertyDescriptor(err, 'message')

you will get this

{ value: 'lol, error testing',
  writable: true,
  enumerable: false,
  configurable: true }

Of particular interest is the fact that enumerable is marked as false. I’m sure there are good justifications for this, I’m just not aware of them.

In order to successfully stringify an Error in JavaScript you need to tell the JSON.stringify function which properties you’d like, explicitly.

If you do this

var err = new Error('more error testing')

followed by this

JSON.stringify(err, Object.getOwnPropertyNames(err))

you will get something like this

'{"stack":"Error: more error testing\\n    at repl:1:11\\n    at sigintHandlersWrap (vm.js:22:35)\\n    at sigintHandlersWrap (vm.js:73:12)\\n    at ContextifyScript.Script.runInThisContext (vm.js:21:12)\\n    at REPLServer.defaultEval (repl.js:346:29)\\n    at bound (domain.js:280:14)\\n    at REPLServer.runBound [as eval] (domain.js:293:12)\\n    at REPLServer.<anonymous> (repl.js:545:10)\\n    at emitOne (events.js:101:20)\\n    at REPLServer.emit (events.js:188:7)","message":"more error testing"}'

Now that looks like the data that we set out to log.