[Fixed] NodeJs: Cannot set headers after they are sent to the client

Issue

I’m creating a simple valid login check in the route with validating jwt token

In the route:

router.post("/contacts", async function (req, res, next) {
  await checkLogin(req, res, next);
  res.send(userController.getContacts());
});

async function checkLogin(req, res, next) {
  const loggedIn = await userController.isLoggedIn(req);
  if (!loggedIn) {
    res.status(401).json({ error: "You are not authorized" });
  }
}

In the controller

async function isLoggedIn(req) {
  let token = req.header("authorization");
  if (!token) {
    return false;
  }
  token = token.substring(7); //remove Bearer from the beginning
  return jwt.verify(token, tokenSecret, function (err, decoded) {
    if (typeof decoded == "object" && decoded.email == req.body.email) {
      return true;
    } else {
      return false;
    }
  });
}

It’s working fine with valid and invalid token but with invalid token I get this log

UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

If I remove status(401) this error log doesn’t show up. Why does it happen and how to set HTTP STATUS to 401 without triggering this error log?

Solution

You’re calling checkLogin, which sets a status code and replies to the request. After checkLogin finishes, the response has already been sent, and you can’t send a second response.

What you’re missing is some conditional logic that would abort processing in case the user is not logged in. Usually, this is implemented in middlewares – a middleware can intercept the request before it reaches the handler.

I can see that your checkLogin implementation already looks a bit like a middleware function: it accepts a next argument. You can turn it into one like this:

async function checkLogin(req, res, next) {
  const loggedIn = await userController.isLoggedIn(req);
  if (!loggedIn) {
    res.status(401).json({ error: "You are not authorized" });
    return;
  }
  next();
}
router.post("/contacts", checkLogin, function (req, res, next) {
  res.send(userController.getContacts());
});

This tells express.js to evaluate checkLogin middleware first, and only pass control to the handler when the middleware calls next(). If it never calls next – control is never passed, so the "main logic" for authenticated users will never run.

Read more about middlewares here: http://expressjs.com/en/api.html#middleware-callback-function-examples

Leave a Reply

(*) Required, Your email will not be published