Search code examples
javascriptnode.jsexpresspromisebluebird

NodeJS Bluebird promise created in a handler but was not returned from it


I have the following nodejs code as an express middleware function

Middleware.is_authenticated = function(req, res, next)
{
    if(req.system_session == undefined || req.system_session.login_status == false) return res.status(401).send({errors: true, error: 'invalid_session', session: req.system_session}).end();

    Session.validate_session(req.system_session, req.ip, req.headers['user-agent'])

    .then(function(session)
    {
        return next();
    })

    .catch(function(err)
    {
        req.system_session.destroy();
        return res.status(401).send({errors: true, error: err.message, session: req.system_session}).end();
    });
};

My Session.validate_session code:

Session.validate_session = function(user, user_ip, user_agent)
{
    return new Promise(function(resolve, reject)
    {
        if(user.user_id == null || user.user_name == null || user.user_rank == null || user.user_session == null || user_ip == null || user_agent == null) return reject(new Error('invalid_session'));

        new api_session({user_session: user.user_session}).fetch({columns: ['user_id', 'user_name', 'user_rank', 'user_session', 'user_ip', 'user_agent']})

        .then(function(result)
        {
            if(result == null) return reject(new Error('invalid_session'));

            return result.toJSON();
        })

        .then(function(session)
        {
            if(session == null || session.user_id != user.user_id || session.user_name != user.user_name || session.user_rank != user.user_rank || session.user_ip != user_ip || session.user_agent != user_agent) return reject(new Error('invalid_session'));

            return resolve(session);
        })

        .catch(function(err)
        {
            return reject(err);
        });
    });
};

Everything is working exactly as it should, everything is executing flawlessly with the database, as well as resolving the 'next' route and even displaying the response. But no matter what I try I keep getting 'a promise was created in a handler but was not returned from it'

Warning: a promise was created in a handler at middleware.js:12:11 but was not returned from it

at new Promise (/Users/Bill/Documents/app/node_modules/bluebird/js/release/promise.js:77:14)
at Object.Permission.permission_list (/Users/Bill/Documents/app/app/security/permission.js:8:10)
at PermissionService.permission_list (/Users/Bill/Documents/app/app/http/services/permission_service.js:6:14)
at Layer.handle [as handle_request] (/Users/Bill/Documents/app/node_modules/express/lib/router/layer.js:95:5)
at next (/Users/Bill/Documents/app/node_modules/express/lib/router/route.js:131:13)
at /Users/Bill/Documents/app/app/http/middleware.js:12:11
at processImmediate [as _immediateCallback] (timers.js:383:17)

From previous event:
at Middleware.is_authenticated (/Users/Bill/Documents/app/app/http/middleware.js:10:4)
at Layer.handle [as handle_request] (/Users/Bill/Documents/app/node_modules/express/lib/router/layer.js:95:5)
at next (/Users/Bill/Documents/app/node_modules/express/lib/router/route.js:131:13)
at Route.dispatch (/Users/Bill/Documents/app/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/Users/Bill/Documents/app/node_modules/express/lib/router/layer.js:95:5)
at /Users/Bill/Documents/app/node_modules/express/lib/router/index.js:277:22
at Function.process_params (/Users/Bill/Documents/app/node_modules/express/lib/router/index.js:330:12)
at next (/Users/Bill/Documents/app/node_modules/express/lib/router/index.js:271:10)
at clientSession (/Users/Bill/Documents/app/node_modules/client-sessions/lib/client-sessions.js:630:5)
at Layer.handle [as handle_request] (/Users/Bill/Documents/app/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/Users/Bill/Documents/app/node_modules/express/lib/router/index.js:312:13)
at /Users/Bill/Documents/app/node_modules/express/lib/router/index.js:280:7
at Function.process_params (/Users/Bill/Documents/app/node_modules/express/lib/router/index.js:330:12)
at next (/Users/Bill/Documents/app/node_modules/express/lib/router/index.js:271:10)
at SendStream.error (/Users/Bill/Documents/app/node_modules/express/node_modules/serve-static/index.js:121:7)
at emitOne (events.js:77:13)
at SendStream.emit (events.js:169:7)
at SendStream.error (/Users/Bill/Documents/app/node_modules/express/node_modules/send/index.js:275:17)
at SendStream.onStatError (/Users/Bill/Documents/app/node_modules/express/node_modules/send/index.js:392:12)

If anyone has any solutions that would be great. I have many promises in my project that are all extremely similar and those are all working great but I just can't get my head around this one.


Solution

  • This comes from the mix of promise-based code (Session.validate_session) with callback-based code (next). It would be expected that the next() call either is not asynchronous (doesn't create any promises) or returns that promise, which it doesn't (blame express).

    There are two ways to avoid the warning:

    • Let express do the error handling, and call next with an error if one occurs. In this case, you can use the .asCallback method:

      Middleware.is_authenticated = function(req, res, next) {
          if (req.system_session == undefined || req.system_session.login_status == false)
              var promise = Promise.reject(new Error('invalid_session'));
          else
              var promise = Session.validate_session(req.system_session, req.ip, req.headers['user-agent']);
      
          promise.asCallback(next);
      };
      
    • As the warning explanation says, you can explicitly return null instead of returning the undefined value that next() yields:

      Middleware.is_authenticated = function(req, res, next) {
          if (req.system_session == undefined || req.system_session.login_status == false)
              var promise = Promise.reject(new Error('invalid_session'));
          else
              var promise = Session.validate_session(req.system_session, req.ip, req.headers['user-agent'])
              .catch(function(err) {
                  req.system_session.destroy();
                  throw err;
              });
      
          promise.then(function(session) {
              next();
              return null;
          }, function(err) {
              res.status(401).send({errors: true, error: err.message, session: req.system_session}).end();
          });
      };