ERR_HTTP_HEADERS_SENT: Cannot set headers after they are sent to the client
Asked Answered
S

17

130

I'm facing this weird issue in NodeJS when using with Passport.js, Express and Mongoose. Basically, I get an error saying "Cannot set headers after they are sent to the client" even though I don't send more than one header.

I've read other posts and tried them out as well, and none of them worked.

I've dug through github issues and I can't seem to find a solution. I get the problem that this error is triggered when I send multiple response headers, but the fact is that I am not sending multiple headers. It seems just weird.

This is my stack trace:

(node:9236) DeprecationWarning: current URL string parser is deprecated, and will be removed in a future version. To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.

Server Running on port 5000
MongoDB Connected Error
[ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
  at validateHeader (_http_outgoing.js:503:11)
   at ServerResponse.setHeader (_http_outgoing.js:510:3)
   at ServerResponse.header (/Users/lourdesroashan/code/github/devlog/node_modules/express/lib/response.js:767:10)
   at ServerResponse.json (/Users/lourdesroashan/code/github/devlog/node_modules/express/lib/response.js:264:10)
   at Profile.findOne.then.profile (/Users/lourdesroashan/code/github/devlog/routes/api/profile.js:27:30)
   at <anonymous>

This is my server code:

router.get("/userprofile", passport.authenticate('jwt', { session: false }), (req, res) => {

  Profile.findOne({ user: req.user.id }).then(profile => {
    if (!profile) {
      return res.status(404).json({ error: "No Profile Found" });
    }
    else {
      res.json(profile);
    }
  }).catch(err => {
    console.log(err);
  })
});

I understand what the error means, but from what I know, I don't think I am sending multiple headers, I even checked by console.log that only one of the blocks is run.

Thank you so much in advance! :)

Full Code at: https://github.com/lourdesr/devlog

EDIT:

I figured it out. It was a problem in my passport.js while trying to get the authenticated user. I forgot to use 'return' on the 'done' method, which had caused it. Just added the return statement and it worked!

Shiv answered 31/8, 2018 at 20:32 Comment(3)
It seems unlikely that the server code you show is causing the error about headers being sent. There must be some other code that is causing that error. That particular error occurs whenever you try to send more than one response to the same request and is usually caused by improper asynchronous code. – Oro
Since you found a separate solution from the jfriend00's suggestions, please post your own answer to your question and accept it. – Excretory
Missing return keyword was also my problem... thanks for posting your edit! πŸ˜ƒ – Swallowtail
O
218

That particular error occurs whenever your code attempts to send more than one response to the same request. There are a number of different coding mistakes that can lead to this:

  1. Improperly written asynchronous code that allows multiple branches to send a response.
  2. Not returning from the request handler to stop further code in the request handler from running after you've sent a response.
  3. Calling next() when you've already sent a response.
  4. Improper logic branching that allows multiple code paths to execute and attempt to send a response.

The code you show in your question does not appear like it would cause that error, but I do see code in a different route here that would cause that error.

Where you have this:

if (!user) {
  errors.email = "User not found";
  res.status(404).json({ errors });
}

You need to change it to:

if (!user) {
  errors.email = "User not found";
  res.status(404).json({ errors });
  // stop further execution in this callback
  return;
}

You don't want the code to continue after you've done res.status(404).json({ errors }); because it will then try to send another response.


In addition, everywhere you have this:

if (err) throw err;

inside an async callback, you need to replace that with something that actually sends an error response such as:

if (err) {
    console.log(err);
    res.sendStatus(500);
    return;
}

throwing inside an async callback just goes back into the node.js event system and isn't thrown to anywhere that you can actually catch it. Further, it doesn't send a response to the http request. In otherwords, it doesn't really do what the server is supposed to do. So, do yourself a favor and never write that code in your server. When you get an error, send an error response.

Or, you can send to centralized Express error handling:

if (err) {
    console.log(err);
    next(err);
    return;
}

And, then you can catch all of these in an Express error handler.


Since it looks like you may be new here, I wanted to compliment you on including a link to your full source code at https://github.com/lourdesr/devlog because it's only by looking at that that I was able to see this place where the error is occuring.

Oro answered 31/8, 2018 at 20:59 Comment(5)
@Shiv - Did this explain it to you and answer your question? – Oro
It did give a lot more clarify. However, after a few hours, I was able to figure out the problem. It was lurking around in my passport.js file, while trying to get the authenticated user - I did not add a return statement in one of the "done" methods, this had caused the multiple headers error. Thank you for helping out! – Shiv
This is a good example of the pitfalls of the pre-async/await way of doing things in Node – Jud
instead of crafting an error response every time you want to throw an error you can use regular throw so long as you have a small middleware to bubble it up to a top-level error handler. e.g. ``` export default function asyncMiddleware(fn: RequestHandler) { return (req: Request, res: Response, next: NextFunction): void => { Promise.resolve(fn(req, res, next)).catch(next) } } ``` – Ameline
@Ameline - Yes, several options covered here async/await error handling in Express. – Oro
U
23

I was receiving this error because of a foolish mistake on my part. I need to be more careful when referencing my other working code. The truly embarrassing part is how long I spent trying to figure out the cause of the error. Ouf!

Bad:

return res
  .send(C.Status.OK)
  .json({ item });

Good:

return res
  .status(C.Status.OK)
  .json({ item });
Urushiol answered 5/8, 2019 at 9:31 Comment(3)
I don't see a difference between your good and bad example. @TheDarkIn1978 – Thrust
The difference is the "bad" example I'm calling .send("ok") while on the "good" example i'm calling .status("ok"). Chaining a .json call after .send caused the error. It was a careless copy/paste mistake from some of my other code. – Urushiol
cant believe so many people making the same mistake and not being able to spot the difference – Aurie
D
16

Use ctrl + F hotkey and find all 'res.' keywords then replace them with 'return res.',

change  all 'res.' to  'return res.'

something like this:

res.send() change to --> return res.send()

maybe you have 'res.' in some block, like if() statement

Defect answered 6/5, 2021 at 13:6 Comment(0)
S
6

Sorry for the Late response, As per the mongoose documentation "Mongoose queries are not promises. They have a .then() function for co and async/await as a convenience. However, unlike promises, calling a query's .then() can execute the query multiple time"

so to use promises

mongoose.Promise = global.Promise //To use the native js promises

Then

var promise = Profile.findOne({ user: req.user.id }).exec()
promise.then(function (profile){
    if (!profile) {
      throw new Error("User profile not found") //reject promise with error
    }
    return res.status(200).json(profile) //return user profile      
}).catch(function (err){
    console.log(err); //User profile not found
    return res.status(404).json({ err.message }) //return your error msg
})

here is an nice article about switching out callbacks with promises in Mongoose

and this answer on mongooses promise rejection handling Mongoose right promise rejection handling

Sixteenmo answered 9/5, 2019 at 10:10 Comment(1)
Note that global.Promise is the default implementation since Mongoose 5: "If you see mongoose.Promise = global.Promise in code that uses Mongoose 5, please delete it. Mongoose 5 uses native promises by default, so that code does nothing in Mongoose 5." REF – Kinslow
N
6

There is a simple fix for the node error [ERR_HTTP_HEADERS_SET]. You need to add a return statement in front of your responses to make sure your router exits correctly on error:

router.post("/", async (req, res) => {
    
    let user = await User.findOne({email: req.body.email});   
    if (!user) **return** res.status(400).send("Wrong user");
    
});
Nellie answered 15/12, 2020 at 22:17 Comment(0)
H
5

Because of multiple response sending in your request. if you use return key word in your else condition your code will run properly

if (!profile) {
    return res.status(404).json({ error: "No Profile Found" });
}
else {
    **return** res.json(profile);
}
Heartworm answered 14/12, 2020 at 7:11 Comment(0)
G
3

Where you have this:

if (!user) {
  errors.email = "User not found";
  res.status(404).json({ errors });
}

You need to change it to:

if (!user) {
  errors.email = "User not found";
  return res.status(404).json({ errors });
}
Gamo answered 4/7, 2022 at 12:20 Comment(0)
C
2

I got the same error using express and mongoose with HBS template engine. I went to Expressjs and read the docs for res.render, and it says // if a callback is specified, the rendered HTML string has to be sent explicitly. So I wasnt originally sending my html explicitly in the callback,. This is only for a contact form btw, not login info, albeit GET

//Original
let { username, email } = req.query;  //My get query data easier to read

res.status(200).render('index', { username, email });

//Solution without error. Second param sending data to views, Third param callback

res.status(200).render('index', { username, email }, (err, html)=>{
      res.send(html);
 });
Carrol answered 14/8, 2019 at 20:10 Comment(0)
N
2

This also happen when you tries to send the multiple response for a same request !! So make sure you always use return keyword to send response to client inorder to stop the further processing !!

Nassir answered 14/4, 2022 at 17:53 Comment(0)
D
1

In react, if your are calling the function in useEffect hook, make sure to add a dependency to the dependency Array.

Derte answered 2/2, 2022 at 8:37 Comment(0)
G
1

I had this error from an if statement not having an else block.

if(someCondition) {
await () => { } 

}

await () => { } 

I changed the above to this below and solved my issue

if(someCondition) {
await () => { } 

} else { 
await () => { } 
}


Greysun answered 9/4, 2022 at 20:19 Comment(0)
C
1

For me, I accidentally put a res.status inside of a for loop. So my server would trigger the error the second time a res.status was returned. I needed to put the res.status outside of the for loop so it would only trigger once within the function.

Choreographer answered 30/8, 2022 at 18:59 Comment(0)
G
1
  1. First of all : make sure you didn't miss any asynchronous action without an async/await or use promises/callbacks.
  2. Then attach any res with the return keyword : return res.status(..).json({});
  3. And finally which was my problem: don't use return res.sendStatus if you always have some return res... inside a callback function, but you can always do a retun res.status();
    in my case it was :
    users.save((err,savedDoc){
    if(err) return res.status(501).json({})
    res.status(200).json({});
    });
    return res.status(500); // instead ofdoing return res.sendStatus(500)
Gynecology answered 7/12, 2022 at 2:35 Comment(0)
A
0

you have to enable Promises in your programm, in my case i enabled it in my mongoose schema by using mongoose.Promise = global.Promise . This enables using native js promises.

other alternatives to this soloution is :

var mongoose = require('mongoose');
// set Promise provider to bluebird
mongoose.Promise = require('bluebird');

and

// q
mongoose.Promise = require('q').Promise;

but you need to install these packages first.

Adham answered 11/2, 2020 at 16:19 Comment(0)
F
0

My problem besides not returning, i was forgetting to await an asynchronous function in the handler. So handler was returning and after a bit the async function did its thing. πŸ€¦πŸ»β€β™€οΈ

Before:

req.session.set('x', {...});
req.session.save();
return req.status(200).end();

When i needed to await:

req.session.set('x', {...});
await req.session.save();
return req.status(200).end();
Fotinas answered 13/10, 2021 at 11:42 Comment(0)
H
0

I'm putting this here for anyone else who has the same problem as me- this happened to me because I'm using the next() function without a return preceding it. Just like a lot of the other answers state, not using return with your response will / can cause / allow other code in the function to execute. In my case, I had this:

app.get("/customerDetails", async (req, res, next) => {

    //  check that our custom header from the app is present
    if (req.get('App-Source') !== 'A Customer Header') next(); 

    var customerID = req.query.CustomerID

    var rows = await get_customer_details(customerID);

    return res.json(rows);
});

In my case, I forgot to include the header in my request, so the conditional statement failed and next() was called. Another middleware function must have then been executed. After the middleware finishes, without a return, the rest of the code in the original middleware function is then executed. So I simply added a return before my next() call:

//  serve customer details payload
app.get("/customerDetails", async (req, res, next) => {

    //  check that our custom header from the app is present
    if (req.get('App-Source') !== 'A Customer Header') return next();

    var customerID = req.query.CustomerID

    var rows = await get_customer_details(customerID);

    return res.json(rows);
});
Humidify answered 7/12, 2022 at 20:11 Comment(0)
A
0

Checked whether there are another res.json() exist first.

My issue are same with you, The reason caused my error was there are another res.json() in my code. In express there are only allow one res.xxx exist in one router path, when the program are running at the second res.json() it will be occur error, and gives you some error tips with "Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client".

Amortization answered 19/4 at 3:54 Comment(0)

© 2022 - 2024 β€” McMap. All rights reserved.