Express.js Cannot read property 'req' of undefined

I also experienced this error, and after chewing on the error message for a bit and staring at my code for too long, It clicked.

I (and the asker above) had code that looked like this:

  somethingAsync
  .then(res.send) // <-- storing send() divorced from parent object "res"
}

The problem is that when res.send gets called later, it is divorced from its dynamic scope--which means that calls to this inside of the send method, which would normally refer to res, now refer to global. Evidently, res.send contains a reference to this.req, given the error message.

An equivalent scenario:

res = {
    send: function() { 
        console.log(this.originalMessage.req);
    },
    originalMessage: { req: "hello SO" },
};

res.send();
// # "hello SO"
const x = {};
x.send = res.send;
x.send();
// # Uncaught TypeError: Cannot read property 'req' of undefined

Or, to put it another way:

new Promise(_ => _())
.then(_ => console.log(this.msg.req));
// # Uncaught (in promise) TypeError: Cannot read property 'req' of undefined

Two solutions:

handler(req, res) {
  somethingAsync
  .then(() => res.send())
   // ^ send will be called as a method of res, preserving dynamic scope
   // that is, we're storing a function that, when called, calls
   // "res.send()", instead of storing the "send" method of "res" by itself.
}

And a theoretically more idiomatic approach is

handler(req, res) {
  somethingAsync
  .then(res.send.bind(res))
  //^ res is explicitly bound to the saved function as its dynamic scope
}

Pernicious little gotcha of dynamic scoping, that one. Seems like express should ditch the dynamic scoping there, or at least throw a better error...


When you passed res.send as an argument to the getContents function (panels.getContents(req.query.content, user.innId, res.send);) it is passed in a new variable and you called it callback.

So res.send is now stripped from it's original/supposed-to-be lexical context/binding which is the res object in this case, and it appears that res.send uses the req property in the res object using this.req.

What you've done here is unintentionally changing the value of this for the function send to be the global object.

Solution

there are two ways to fix this problem

  • panels.getContents(req.query.content, user.innId, () => res.send);

// OR

  • panels.getContents(req.query.content, user.innId, res.send.bind(res));

in the second solution, you bind the function res.send to always treat any value of this to be the res object (bind it to the res object)


try this in index.js

panels.getContents(req.query.content, user.innId, res);

and into panels.js

exports.getContents = function(panelName, innId, response) {
    var response = "";
    switch (panelName) {
        case 'tenants':
            con.query(queryString, queryParams, function(err, rows) {
                if (err) {
                    handle(err);
                } else {
                    if (rows.length == 0) {
                        var tenants = 0;
                        var debtors = 0;
                    } else {
                        var tenants = rows[0].tenants;
                        var debtors = rows[0].length;
                    }
                    response.send(convertToHTMLTable(rows));
                }
            });
            break;

        /* other cases here */

        default:
            error += "Invalid Request";
            response.send(error)
    }
}