Kiechlus
Kiechlus

Reputation: 1237

Sending an Error Object from a spawned child-process over an IPC-channel

I enabled communication between parent and child process in order to send JSON as follows:

Child:

try {  
  var price1 = parseInt(process.argv[2]);
  if (!price1) {
     throw new Error('Price in calculations.js undefined');
  }
  var result = {
    'timeStamp' : Date(),
    'prices' : { 'player1' : price1, 'player2' : 666}
  };
  process.send(result);
} catch (e) {
  // In case of an error, I get here as expected.
  process.send(e);
}

Parent:

var spawn = require('child_process').spawn;
var child = spawn('node', ['calculations.js', 333],  {stdio: [null,null,'pipe','ipc']});
child.on('message', function(data) {    
  if (data instanceof Error) {
    // In case of an error, this is never reached.
  } else {
    // do sthing with JSON object.
  }
});

The JSON thing works fine. But if I provoke an error, it doesn't work. I want to send the entire error-object (with message and stack-trace) from child to parent. But it doesn't seem to be an instance of error what I am sending.

Upvotes: 7

Views: 2141

Answers (2)

Esailija
Esailija

Reputation: 140228

Processes don't share memory so the only way to communicate is with strings, objects are JSON serialized on send and JSON parsed back on receive. Error objects don't serialize well by default:

JSON.stringify(new Error())
"{}"

Also, JSON parsed object is untyped so instanceof cannot work.

You can make serialization hook for errors objects:

Error.prototype.toJSON = function() {
    var ret = {
        name: this.name,
        message: this.message,
        stack: this.stack,
        __error__: true
    };
    // Add any custom properties such as .code in file-system errors
    Object.keys(this).forEach(function(key) {
        if (!ret[key]) {
            ret[key] = this[key];
        }
    }, this);
    return ret;
};

After that method definition error objects serialize better:

 JSON.stringify(new Error())

"{"name":"Error","message":"","stack":"Error\n    at <anonymous>:2:16\n    at Object.InjectedScript._evaluateOn (<anonymous>:762:137)\n    at Object.InjectedScript._evaluateAndWrap (<anonymous>:695:34)\n    at Object.InjectedScript.evaluate (<anonymous>:609:21)","__error__":true}"

Then reconstruct it automatically:

function getMessageReceiver(fn) {
    return function(data) {
        var result = data;
        if (data && data.__error__) {
            result = new Error();
            result.message = data.message;
            result.stack = data.stack;
            result.name = data.name;
            Object.keys(data).forEach(function(key) {
                if (!result[key]) {
                    result[key] = data[key];
                }
            });
        }
        return fn.call(this, result);
    }
}

And finally:

child.on('message', getMessageReceiver(function(data) {    
  if (data instanceof Error) {
    console.log(data.stack); // Stack is from child process
  } else {
    // do sthing with JSON object.
  }
}));

Upvotes: 6

Naeem Shaikh
Naeem Shaikh

Reputation: 15725

This is something I tried and works,

var Error=function(mes){
    this.message=mes;
};

try {  
  var price1 = parseInt(process.argv[4]);
 if (!price1) {
     throw new Error('Price in calculations.js undefined');
 }
  var result = {
    'timeStamp' : Date(),
    'prices' : { 'player1' : price1, 'player2' : 666}
  };  console.log("inside try");
  process.send(result);
} catch (e) {
  // In case of an error, I get here as expected.
  console.log("inside catch");
  process.send(e);
}

First create object Error before throwing it, otherwise it just pass an empty object which is not instanceof Error.

And the parent

var child = require('child_process').fork(__dirname + '/SO2.js', [333],  {stdio: [null,null,'pipe','ipc']});

child.on('message', function(data) {
  if(data.timeStamp){
    console.log("result received ");
  } 
  else{
    // do sthing with JSON object.
    console.log("user defined error messege"+data.message + JSON.stringify(data));
  }
});

Upvotes: 1

Related Questions