3

Error handling in Node. Argh!

I'm trying to layout a basic Node app like this...

Cluster -> Worker -> Server Domain -> Express Request Domain

So, if an error is thrown 18 layers deep into a call stack because someone misspelled their name on a login form, the entire server doesn't crash.

Here's some basic code to simulate the worker part:

var domain, server;

domain = require('domain');
server = domain.create();

server.on('error', function(e) {
    console.log('total meltdown...', e.stack);
});

server.run(function() {

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

    express.configure(function() {

        // Domain on EVERY request
        express.use(function(req, res, next) {
            var d = domain.create();

            d.on('error', function(e) {
                console.log('fired REQUEST error', e.stack);
                next(e);
            });

            d.run(next);

        });

        // Generic error handler
        express.use(function(e, req, res, next) {
            res.status(500);
            res.end('oops');
        });

        // Serve the request with a blatent error
        express.get('/', function(req, res) {

            this_function_does_not_exist();
            res.end('we will never get here');

        });
    });

    // Fire 'er up
    express.listen(3000);
});

What I'm expecting...

I curl http://localhost:3000/, get a nice little 'oops' error, and see 'fired REQUEST error' and the error stack in the console.

What actually happens...

I get this both as the browser response, and in the console...

ReferenceError: this_function_does_not_exist is not defined at /Stuff/test.js:38:13 at callbacks (/Stuff/node_modules/express/lib/router/index.js:161:37) at param (/Stuff/node_modules/express/lib/router/index.js:135:11) at pass (/Stuff/node_modules/express/lib/router/index.js:142:5) at Router._dispatch (/Stuff/node_modules/express/lib/router/index.js:170:5) at Object.router (/Stuff/node_modules/express/lib/router/index.js:33:10) at next (/Stuff/node_modules/express/node_modules/connect/lib/proto.js:190:15) at next (/Stuff/node_modules/express/node_modules/connect/lib/proto.js:192:9) at b (domain.js:183:18) at Domain.run (domain.js:123:23)

Now why would it go and do something like that?

aynber
  • 22,380
  • 8
  • 50
  • 63
Lee Benson
  • 11,185
  • 6
  • 43
  • 57

1 Answers1

2

Ok, solved - Express has a try/catch block, which is getting to my non-existent function call first.

To have the domain catch it, it needs to be taken out of the current call stack, like...

        process.nextTick(function() {
            this_function_does_not_exist();
            res.end('we will never get here');
        });

THEN the domain will grab it.

Lee Benson
  • 11,185
  • 6
  • 43
  • 57
  • i appreciate you pointing out why - but you don't actually give sample code that shows how "it needs to be taken out of the current call stack" – Tom Carchrae Oct 16 '13 at 13:25
  • 1
    if it's part of the current call stack, Express will catch it, and it won't 'bubble up' to the domain handler. Trouble is, we don't always know when that will be, and it could be outside of a function that calls res.send()/throws anything back to the browser. So by using process.nextTick, we are effectively side-stepping the Express stack, and running it independently at the END of the current call stack... but still within the context of the domain. So, an error fires, the domain picks it up, and throws it back to res.send which is within context of my original sample code. Does that help? – Lee Benson Oct 16 '13 at 16:34
  • thanks - i follow what you are saying. you want to throw an exception in nextTick(). – Tom Carchrae Oct 17 '13 at 00:52