4

At the very end of my server.js file I have the following code:

app.use(logErrors);

function logErrors (err: Error, req: Request, res: Response, next: NextFunction) {
    console.log(err);
    mongoDal.log(err.message, err);
    next(err);
}

but this code doesn't get reached when an error occurs.

I also have the Node.js error handling function, which doesn't get reached either:

process.on('uncaughtException', function (err: Error) {
    try {
        console.log(err);
        mongoDal.log(err.message, err);
    } catch (err) {

    }
});

This is the code that produces the error:

app.get('/test_feature', function (req: Request, res: Response) {
    makeError();

    res.send("Done");
});

function makeError(){
    throw new Error("asdasdad");
}

BTW the error does get written into the console (but not by a function of mine), and the app doesn't crash.

What I'm trying to make is a generic solution that will catch every unhandled exception in one place.

What am I doing wrong?

Alon
  • 10,381
  • 23
  • 88
  • 152
  • How are you defining yout requests?? app.get(req, res)??? – Lucas Katayama Nov 23 '16 at 20:02
  • @LucasKatayama yes – Alon Nov 24 '16 at 09:52
  • 1
    Express already has a default error handler, which is probably the one logging the error. My guess is that you're installing your error handler at a point where it's not the very last middleware, but I can't say for sure without knowing how your app is structured (is `server.js` the app entry point, or do you start the app through some other file?) – robertklep Nov 27 '16 at 13:04
  • @robertklep I start my app like that: "node server.js", this is the first file that gets executed. – Alon Nov 27 '16 at 19:19
  • @Alon hmm, that sounds right. I'm fairly certain that for some reason, your error handler isn't being registered properly and the default error handler kicks in. You could try and run your app in debug mode to see if that sheds any light on what's happening: `DEBUG=* node server.js` – robertklep Nov 27 '16 at 19:24
  • @robertklep I've debugged it before and it hasn't shed any light on what's happening. I'll be glad to hear more suggestions. – Alon Nov 28 '16 at 14:49
  • @Alon can you add the debug log for a request to `/test_feature` to your question? – robertklep Nov 28 '16 at 14:51

4 Answers4

9

Below is the short working example with handling for 3 types of errors: 1) passed to next() handler, 2) throw-ed inside route handler, 3) unhandled error inside callback of some function called from route handler.

(1) and (2) are captured using custom error middleware handler (A) and (3) is captured by uncaughtException handler (B).

Express has own error handler which outputs error if it gets the control using the chain of next() calls (i.e. if there is no custom error handler or if it passes the control further using next(err, req, res, next)). That's why you still getting an error message in console even if youyr handler is not triggers.

If you try to run the example for cases (1) and (2), you'll see the error outputs twice - by custom handler (A) and by default Express error handler.

'use strict';

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

var server = app.listen(8080, function() {
  console.log('* Server listening at ' + server.address().address + ':' + server.address().port);
});

// Example 1: Pass error in route handler to next() handler
app.use('/1', function(req, res, next) {
  console.log('* route 1');
  next(new Error('* route 1 error'));
});

// Example 2: throw the error in route handler
app.use('/2', function(req, res, next) {
  console.log('* route 2');
  throw new Error('route 2 error');
});

// Example 3: unhandled error inside some callback function
app.use('/3', function(req, res, next) {
  console.log('* route 3');
  setTimeout(function(){
    throw new Error('route 3 error');
  }, 500);
});

// Error handler A: Express Error middleware
app.use(function(err, req, res, next) {
  console.log('**************************');
  console.log('* [Error middleware]: err:', err);
  console.log('**************************');
  next(err);
});

// Error handler B: Node's uncaughtException handler
process.on('uncaughtException', function (err) {
  console.log('**************************');
  console.log('* [process.on(uncaughtException)]: err:', err);
  console.log('**************************');
});

Node version: v7.2.0

Typical error is to place error handlers before route definitions, but according to your description that's not the case.

To locate the issue you can try to reduce your own code to same size as mine and I think, the problem will become obvious.


UPDATE

Moreover, if I try to run the code you provided (with trivial modifications), it works for me:

'use strict';

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

var server = app.listen(8080, function() {
  console.log('* Server listening at ' + server.address().address + ':' + server.address().port);
});

//process.on('uncaughtException', function (err: Error) {
process.on('uncaughtException', function (err) {
  try {
    console.log('*** uncaughtException:', err);
    //mongoDal.log(err.message, err);
  } catch (err) {

  }
});

//app.get('/test_feature', function (req: Request, res: Response) {
app.get('/test_feature', function (req, res) {
  makeError();
  res.send("Done");
});

function makeError(){
  throw new Error("asdasdad");
}

app.use(logErrors);

//function logErrors (err: Error, req: Request, res: Response, next: NextFunction) {
function logErrors (err, req, res, next) {
  console.log('*** logErrors:', err);
  //mongoDal.log(err.message, err);
  next(err);
}

The result is (stack traces are truncated):

* Server listening at :::8080
*** logErrors: Error: asdasdad
    at makeError (/home/alykoshin/sync/al-projects/dev/nmotw/400-express-error-handling/main-stackoverflow.js:32:9)
...........
Error: asdasdad
    at makeError (/home/alykoshin/sync/al-projects/dev/nmotw/400-express-error-handling/main-stackoverflow.js:32:9)
...........

You may see at the beginning the ouput of logErrors handler and then the output of default Express error handler.

alykoshin
  • 866
  • 8
  • 6
  • Another suggestion is that you have another error handler before yours which prints out the error message but does not passes the control further using `next(err)` – alykoshin Nov 27 '16 at 19:48
2

Note: your error handler middleware MUST have 4 parameters: error, req, res, next. Otherwise your handler won't fire.

I was fighting this problem until I've discovered this.

Van SallyOne
  • 833
  • 6
  • 9
0

I think the problem is on the routing definitions

As you said you are using this

app.get('/test_feature', function (req, res) {
    throw new Error("asdasdad");
    res.send("Done");
});

Try using this:

app.get('/test_feature', function (req, res, next) {
    next(new Error("asdasdad"));
});

And then put res.send on the error handler...

Why?

I think throwing a Error inside the function stops the request chain... So it doesn't reach the very ending and then your error handler. If you write as the second way... you move the error forward ... and then reaches your handler...

Lucas Katayama
  • 4,445
  • 27
  • 34
  • I've simplified my code. I actually get an error from a function that I call. I can catch it with a try/catch block, but I don't want to add a try/catch block to every app.get function. I want a generic solution that will catch all unhandled exceptions in one place. – Alon Nov 24 '16 at 10:18
  • Try this http://stackoverflow.com/questions/19690756/how-can-i-wrap-every-express-js-request-in-a-domain-or-trycatch – Lucas Katayama Nov 24 '16 at 10:24
  • As you can see in my code, I've studied this topic. I just don't manage to find what I'm doing wrong. The example you sent looks just like my code to me. I must miss something. – Alon Nov 24 '16 at 10:35
  • A throw inside a route calls next(err) automatically by express. – André Werlang Nov 27 '16 at 20:10
0

If you want the global uncaughtException to run, you need to throw an error at a place where no one is catching, like this:

app.get('/test_feature', function (req: Request, res: Response) {
    setTimeout(function () { throw new Error('oops'); }, 1000);

    res.send("Done");
});

A synchronous throw inside a handler is caught by express, no uncaughtException happens at all as it was caught.

And according to http://expressjs.com/en/guide/error-handling.html, an error-handling middleware is defined after your routes and other middlewares.

app.get('/test_feature', function (req: Request, res: Response) {
    makeError();

    res.send("Done");
});

app.use(logErrors)

If you get the wrong order, there would be no error handler to be called when something is thrown.

André Werlang
  • 5,839
  • 1
  • 35
  • 49