Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to handle exception with node.js domains using express

I want to use Node.js Domains to catch exceptions. It is working so far, but there is one place I can't get domains to catch the exception. exception2 in the callback is caught and handled in the domain.on('error') handler, but exception1 is not caught. The odd thing is that when exception1 is thrown, it doesn't shutdown Node like I would expect. Here is my example app:

var domain = require('domain');
var request = require('request');
var express = require('express');

var serverDomain = domain.create();
serverDomain.on('error', function(err) {
  console.log("Server Domain Error: " + err);
});

var app;

serverDomain.run(function() {
  app = express();
  app.listen(3000);
});

app.use(function(req, res, next) {

  var reqDomain = domain.create();
  reqDomain.add(req);
  reqDomain.add(res);
  reqDomain.on('error', function(err) {
    console.log("Req Domain Error: " + err);
    reqDomain.dispose();
    next(err);
  });

  next();
});

app.get('/', function(req, res) {
  var uri = "http://google.com";

  exception1.go();

  request.get({url:uri, json: {}},
    function (error, response, body) {
      if(response.statusCode === 200) {
        exception2.go();
        res.send('Success getting google response');
      }
    });
});

To get exception2 to execute, I comment out exception 1.

like image 455
user1007983 Avatar asked Nov 05 '12 08:11

user1007983


3 Answers

The problem is that the exception happens during Connect's routing, which has both a try/catch block around its execution, as well as a default error handler which prints out stack trace details when running in a non-production mode. Since the exception is handled inside of Express, it never reaches your outer layer for the domains to handle.

How it differs from exception2 is that the handler function for the '/' route is executed directly by that Connect block, in the same stack as the original call that went through Express. The second exception occurs in a callback, after some I/O operation has returned, and therefore is executed by a stack originating from Node's event loop I/O handler, and so the try/catch of Express isn't available to snag that exception and save the app server. In fact, if you comment out all the domain stuff, and trip exception2 it crashes Node.

Since only unhandled exceptions are routed to the domain mechanism, and since exception1 has a try/catch visible in it's call stack above it, the exception is handled, and not forwarded to the domain.

like image 177
Tim Shadel Avatar answered Sep 28 '22 13:09

Tim Shadel


Connect-domain allows you to catch all errors for connect modules including express.

Connect-domain https://github.com/baryshev/connect-domain

The example for express3: http://masashi-k.blogspot.com/2012/12/express3-global-error-handling-domain.html

like image 43
wf9a5m75 Avatar answered Sep 28 '22 12:09

wf9a5m75


@user1007983

No, in production, the try/catch handling still exists in Connect/Express. The way to solve it is to add your own try/catch block in the "root" which you can then use to emit an "error" event to the domain before connect sends the error response.

try {
    // .. code here ..
} catch (err) {
    domain.emit('error', err);
}

Another way to get around it is to just disconnect from the handler, like wrapping your code in a setImmediate block

like image 4
Issac Avatar answered Sep 28 '22 11:09

Issac