title: Express 4 middleware error handler not being called
body: <p>For certain pages I have custom 500, 404 and 403 error handling in my app. So for instance after an unsuccessful database query I'd go:</p>
  
  <pre><code>return next({status: 404, message: 'Record not found'});
  </code></pre>
  
  <p>or</p>
  
  <pre><code>return next(new Error('Bad things have happened')});
  </code></pre>
  
  <p>In my middleware I have an error handler:</p>
  
  <pre><code>app.use(function (err, req, res, next) {
      // handle error
  });
  </code></pre>
  
  <p>Problem is that the error handler is never called, instead the error callstack is being printed into the browser. I want the handler to render a custom error page.</p>
  
  <p>app.js</p>
  
  <pre><code>var express = require('express')
      , app = express()
      , swig = require('swig')
      , config = require('./lib/config')
      , env = process.env.NODE_ENV || 'development'
      , path = require('path');
  
  config.configure(env);
  
  app.engine('html', swig.renderFile);
  app.set('view cache', false);
  
  swig.setDefaults({
      cache: config.get('swigCache')
  });
  
  app.set('view engine', 'html');
  app.set('views', __dirname + '/lib/views');
  
  require('./lib/util/swig');
  require('./lib/initialisers/mongodb')();
  require('./lib/initialisers/aws')();
  require('./lib/middleware')(app); // first load middleware
  require('./lib/routes')(app); // then routes
  
  var server = app.listen(config.get('port'), function() {
      console.info('config: ' + JSON.stringify(config.getCurrent()));
      console.info('NODE_ENV: ' + env);
      console.info('server running: ' + JSON.stringify(server.address()));
  });
  </code></pre>
  
  <p>routes.js</p>
  
  <pre><code>module.exports = function(app){
  
      app.get('/', require('./views/').index);
      app.get('/blog', require('./views/blog').index);
      app.get('/blog/:slug', require('./views/blog').getBySlug);
  
      app.route('/report/:slug')
          .get(require('./views/report/').index)
          .post(require('./views/report/').doReport);
  
          // Very long file with tons of routes. Simplified version.
  </code></pre>
  
  <p>middleware.js</p>
  
  <pre><code>var express = require('express')
      , app = express()
      , path = require('path')
      , logger = require('morgan')
      , cookieParser = require('cookie-parser')
      , bodyParser = require('body-parser')
      , passport = require('passport')
      , session = require('express-session')
      , mongoStore = require('connect-mongo')(session)
      , compression = require('compression')
      , favicon = require('serve-favicon')
      , config = require('./config')
      , flash = require('connect-flash')
      , multer = require('multer')
      , csrf = require('csurf');
  
  module.exports = function(app) {
  
      app.use(bodyParser.urlencoded({ extended: false }))
      app.use(bodyParser.json());
      app.use(cookieParser());
      app.use(csrf({ cookie: true }));
      app.use(express.static(path.join(__dirname, config.get('staticContentPath')), {
          maxAge: (60 * 60 * 24) * 1000
      }));
  
      app.use(session({
          resave: true,
          saveUninitialized: true,
          secret: 'da755fc0-6882-11e4-9803-0800200c9a66',
  
          cookie: {
              maxAge: 24 * 60 * 60 * 1000 // 24 hrs
          },
  
          store: new mongoStore({
              url: config.getMongoConn()
          })
      }));
  
      app.use(logger('dev'));
      app.use(flash());
  
      /**
       * 301 redirects
       */
      app.use(function(req, res, next) {
  
          var host = req.get('host');
  
          // AWS IP --&gt; http
          if (host == 'xx.xxx.xxx.xxx') {
              return res.redirect(301, config.get('url') + req.originalUrl);
          }
  
          // AWS origin --&gt; http
          if(host == 'xxx-xxx-xxx-xxx-xxx.ap-southeast-2.compute.amazonaws.com'){
              return res.redirect(301, config.get('url') + req.originalUrl);
          }
  
          // www --&gt; http
          if (/^www\./.test(host)) {
              host = host.substring(4, host.length);
              return res.redirect(301, req.protocol + '://' + host + req.originalUrl);
          }
  
          // Trailing slash --&gt; http
          if (req.path.substr(-1) == '/' &amp;&amp; req.path.length &gt; 1) {
              var query = req.url.slice(req.path.length);
              return res.redirect(301, req.path.slice(0, -1) + query);
          }
  
          next();
      });
  
      // Delete expired Mongo sessions from DB
      app.use(function (req, res, next) {
          req.session._garbage = new Date();
          req.session.touch();
          next();
      });
  
      /**
       * Setting Cache control header for Ajax requests to 30 minutes
       */
      app.use(function (req, res, next) {
  
          if(req.xhr){
              res.header('Cache-Control', 'max-age=' + 1800 + ', public');
          }
  
          next();
      });
  
      app.use(compression());
  
      app.use(
          multer({
              dest: config.get('uploads').folders.temp
          })
      );
  
      app.use(passport.initialize());
      app.use(passport.session());
      var initPassport = require('./passport/init');
      initPassport(passport);
  
      app.use(function (req, res, next) {
  
          res.locals = {
              root : 'http://' + req.headers.host,
              sitename : require('./config').get('sitename'),
              config: config.get('env'),
              url : config.get('url'),
              user : req.user,
              flash : req.flash()
          };
  
          next();
      });
  
      app.use(function (err, req, res, next) {
  
          if (err.code !== 'EBADCSRFTOKEN'){
              return next(err);
          }
  
          if(req.xhr){
              return res.ok({payload: null}, '403 invalid csrf token');
          }
  
          // TODO handle CSRF token errors here
          res.status(403);
          res.send('form tampered with')
      });
  
      // This is never called when throwing errors like
      // next(new Error('some error') or
      // next({status: 500, message:'server error'});
      app.use(function (err, req, res, next) {
          console.error(err.stack);
          // render an error page
      });
  };
  </code></pre>