Search code examples
javascriptnode.jsnock

Unhandled Promise Rejection Warning : Error: cant set headers after they are sent


I have following end point which makes axios request to get user information. This end point returns data as I expected.

const axios = require('axios');
const router = require('express').Router();
const config = require('../config');
const constants = require('../constants');
const errors = require('../utils/errors');
const ssoTokenService = require('../utils/sso-token-util'); // takes auth token from req header or cookie

router.get('/', (req, res) => {
  const ssoToken = ssoTokenService.getSsoToken(req);
  if (!ssoToken) {
    res.status(constants.HTTP_UNAUTHORIZED).json({
      errors: [{
        code: 401,
        message: 'sso token is missing in header',
      }],
      message: 'UnAuthorized'
    });
  }
  const httpFetchUserInfo = {
    headers: Object.assign(res._headers, {
      'Content-Type': 'application/json',
      Accept: 'application/json',
      'x-correlation-id': res.locals.xCorrelationID,
      MYSAPSSO2: ssoToken,
    }),
    method: 'GET',
    url: `${config.app.entSapUserUrl}/sapusers`,
    timeout: config.app.enterpriseHTTPTimeout
  };
  axios(httpFetchUserInfo)
    .then((entFetchUserInfoAPIResponse) => {

      res.status(200).json(entFetchUserInfoAPIResponse.data);

    }).catch((err) => {
      let errorList;
      if (err && err.response && err.response.data && err.response.data.errorList) {
        errorList = err.response.data.errorList;
      }
      const error = errors.createEntError(err.message, errorList);
      if (err.response && err.response.status) {
        res.status(err.response.status).json(error);
      } else {
        res.status(constants.HTTP_SERVER_ERROR).json(error);
      }
    });

});
module.exports = router;

But I have following unit tests for this end points

it('verify returns bad request if query is not specified', (done) => {
  interceptor = nock(config.app.entSapUserUrl)
    .get('/sapusers')
    .reply(constants.HTTP_OK, {
      userId: 'ABC456',
      customerId: '',
      firstName: 'ABC',
      lastName: 'KKK',
      branchId: ''
    });

  request(app)
    .get('/user')
    .set({
      'Content-Type': 'application/json',
      MYSAPSSO2: 'hjikgvkjvlhguiohgjklnhguio'
    })
    .expect(constants.HTTP_OK, {
      userId: 'ABC456',
      customerId: '',
      firstName: 'ABC',
      lastName: 'KKK',
      branchId: ''
    })
    .end((err) => {
      if (err) {
        return done(err);
      }
      done();
    });
});

it('verify whether sso token is necessary', (done) => {
  request(app)
    .get('/user')
    .set({
      'Content-Type': 'application/json',
    })
    .expect(constants.HTTP_UNAUTHORIZED,

      {
        errors: [{
          code: 401,
          message: 'sso token is missing in header',
        }],
        message: 'UnAuthorized'
      }

    )
    .end((err) => {
      if (err) {
        return done(err);
      }
      done();
    });
});

If I ran tests all of them passes but in console I can see below error message

✓ verify returns bad request if query is not specified

(node: 41573) UnhandledPromiseRejectionWarning: Error: Can 't set headers after they are sent.
at validateHeader(_http_outgoing.js: 491: 11)
at ServerResponse.setHeader(_http_outgoing.js: 498: 3)
at ServerResponse.header(/Users/c
  42470 / localApp / node_modules / express / lib / response.js: 767: 10)
at ServerResponse.send(/Users/c
  42470 / localApp / node_modules / express / lib / response.js: 170: 12)
at ServerResponse.json(/Users/c
  42470 / localApp / node_modules / express / lib / response.js: 267: 15)
at axios.then.catch.err(/Users/c
  42470 / localApp / server / routes / user.js: 2: 937)
at < anonymous >
  at process._tickCallback(internal / process / next_tick.js: 188: 7)
  (node: 41573) UnhandledPromiseRejectionWarning: Unhandled promise rejection.This error originated either by throwing inside of an async function without a
catch block, or by rejecting a promise which was not handled with.catch().(rejection id: 54)

I'm assuming that I am handling promise rejection in right way in catch block Please Correct me if I'm doing something wrong.


Solution

  • This error means that you are trying to send a response after a response has already been sent. I think this will fix your problem.

    change

    if (!ssoToken) {
        res.status(constants.HTTP_UNAUTHORIZED).json({
          errors: [{
            code: 401,
            message: 'sso token is missing in header',
          }],
          message: 'UnAuthorized'
        });
      }
    

    to

    if (!ssoToken) {
        return res.status(constants.HTTP_UNAUTHORIZED).json({
          errors: [{
            code: 401,
            message: 'sso token is missing in header',
          }],
          message: 'UnAuthorized'
        });
      }
    

    I think you just need to add the return statement and you'll be good to go.