Search code examples
node.jsexpresserror-handlingnode.js-connect

express error middleware not handling errors thrown from promise.done()


If I throw an Error, express renders it nicely using the connect errorHandler middleware.

exports.list = function(req, res){
  throw new Error('asdf');
  res.send("doesn't get here because Error is thrown synchronously");
};

And when I throw an Error within a promise it will be ignored (this makes sense to me).

exports.list = function(req, res){
  Q = require('q');
  Q.fcall(function(){
    throw new Error('asdf');
  });
  res.send("we get here because our exception was thrown async");
};

However, If I throw an Error within a promise and call "done" node crashes, because the exception isn't caught by the middleware.

exports.list = function(req, res){
  Q = require('q');
  Q.fcall(function(){
    throw new Error('asdf');
  }).done();
  res.send("This prints. done() must not be throwing.");
};

After running the above, node crashes with this output:

node.js:201
        throw e; // process.nextTick error, or 'error' event on first tick
              ^
Error: asdf
    at /path/to/demo/routes/user.js:9:11

So my conclusion is that done() isn't throwing the exception, but causes an exception to be thrown elsewhere. Is that right? Is there a way to accomplish what I'm trying - where errors in the promise will be handled by the middleware?

FYI: This hack will catch the exception at the top level, but it's outside of the realm of middleware, so doesn't suite my needs (to nicely render the error).

//in app.js #configure
process.on('uncaughtException', function(error) {
  console.log('uncaught expection: ' + error);
})

Solution

  • Maybe you'll find connect-domain middleware useful for handling async errors. This middleware allows you to handle async errors like a regular errors.

    var
        connect = require('connect'),
        connectDomain = require('connect-domain');
    
    var app = connect()
        .use(connectDomain())
        .use(function(req, res){
            process.nextTick(function() {
                // This async error will be handled by connect-domain middleware
                throw new Error('Async error');
                res.end('Hello world!');
            });
        })
        .use(function(err, req, res, next) {
            res.end(err.message);
        });
    
    app.listen(3131);