Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it not possible to stringify an Error using JSON.stringify?

Reproducing the problem

I'm running into an issue when trying to pass error messages around using web sockets. I can replicate the issue I am facing using JSON.stringify to cater to a wider audience:

// node v0.10.15 > var error = new Error('simple error message');     undefined  > error     [Error: simple error message]  > Object.getOwnPropertyNames(error);     [ 'stack', 'arguments', 'type', 'message' ]  > JSON.stringify(error);     '{}' 

The problem is that I end up with an empty object.

What I've tried

Browsers

I first tried leaving node.js and running it in various browsers. Chrome version 28 gives me the same result, and interestingly enough, Firefox at least makes an attempt but left out the message:

>>> JSON.stringify(error); // Firebug, Firefox 23 {"fileName":"debug eval code","lineNumber":1,"stack":"@debug eval code:1\n"} 

Replacer function

I then looked at the Error.prototype. It shows that the prototype contains methods such as toString and toSource. Knowing that functions can't be stringified, I included a replacer function when calling JSON.stringify to remove all functions, but then realized that it too had some weird behavior:

var error = new Error('simple error message'); JSON.stringify(error, function(key, value) {     console.log(key === ''); // true (?)     console.log(value === error); // true (?) }); 

It doesn't seem to loop over the object as it normally would, and therefore I can't check if the key is a function and ignore it.

The Question

Is there any way to stringify native Error messages with JSON.stringify? If not, why does this behavior occur?

Methods of getting around this

  • Stick with simple string-based error messages, or create personal error objects and don't rely on the native Error object.
  • Pull properties: JSON.stringify({ message: error.message, stack: error.stack })

Updates

@Ray Toal Suggested in a comment that I take a look at the property descriptors. It is clear now why it does not work:

var error = new Error('simple error message'); var propertyNames = Object.getOwnPropertyNames(error); var descriptor; for (var property, i = 0, len = propertyNames.length; i < len; ++i) {     property = propertyNames[i];     descriptor = Object.getOwnPropertyDescriptor(error, property);     console.log(property, descriptor); } 

Output:

stack { get: [Function],   set: [Function],   enumerable: false,   configurable: true } arguments { value: undefined,   writable: true,   enumerable: false,   configurable: true } type { value: undefined,   writable: true,   enumerable: false,   configurable: true } message { value: 'simple error message',   writable: true,   enumerable: false,   configurable: true } 

Key: enumerable: false.

Accepted answer provides a workaround for this problem.

like image 299
Jay Avatar asked Aug 22 '13 21:08

Jay


People also ask

Can you Stringify an error?

Errors and Edge Casesstringify() throws an error when it detects a cyclical object. In other words, if an object obj has a property whose value is obj , JSON. stringify() will throw an error. const obj = {}; // Cyclical object that references itself obj.

Is it bad to use JSON Stringify?

It`s ok to use it with some primitives like Numbers, Strings or Booleans. As you can see, you can just lose unsupported some data when copying your object in such a way. Moreover, JavaScript won`t even warn you about that, because calling JSON. stringify() with such data types does not throw any error.

What does JSON Stringify () do?

stringify() The JSON. stringify() method converts a JavaScript object or value to a JSON string, optionally replacing values if a replacer function is specified or optionally including only the specified properties if a replacer array is specified.

Is there a limit to JSON Stringify?

So as it stands, a single node process can keep no more than 1.9 GB of JavaScript code, objects, strings, etc combined. That means the maximum length of a string is under 1.9 GB. You can get around this by using Buffer s, which store data outside of the V8 heap (but still in your process's heap).


2 Answers

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

seems to work

[from a comment by /u/ub3rgeek on /r/javascript] and felixfbecker's comment below

like image 73
2 revs, 2 users 80% Avatar answered Sep 23 '22 13:09

2 revs, 2 users 80%


You can define a Error.prototype.toJSON to retrieve a plain Object representing the Error:

if (!('toJSON' in Error.prototype)) Object.defineProperty(Error.prototype, 'toJSON', {     value: function () {         var alt = {};          Object.getOwnPropertyNames(this).forEach(function (key) {             alt[key] = this[key];         }, this);          return alt;     },     configurable: true,     writable: true }); 
var error = new Error('testing'); error.detail = 'foo bar';  console.log(JSON.stringify(error)); // {"message":"testing","detail":"foo bar"} 

Using Object.defineProperty() adds toJSON without it being an enumerable property itself.


Regarding modifying Error.prototype, while toJSON() may not be defined for Errors specifically, the method is still standardized for objects in general (ref: step 3). So, the risk of collisions or conflicts is minimal.

Though, to still avoid it completely, JSON.stringify()'s replacer parameter can be used instead:

function replaceErrors(key, value) {     if (value instanceof Error) {         var error = {};          Object.getOwnPropertyNames(value).forEach(function (propName) {             error[propName] = value[propName];         });          return error;     }      return value; }  var error = new Error('testing'); error.detail = 'foo bar';  console.log(JSON.stringify(error, replaceErrors)); 
like image 40
Jonathan Lonowski Avatar answered Sep 23 '22 13:09

Jonathan Lonowski