26

I'm not really sure why I'm getting this error. It's a simple API built on express.js to be able to add and remove posts. The error occurs when I trigger the delete router. I've read that the error typically happens when there are two callbacks, however, I don't seem to be able find any double callbacks.

    _http_outgoing.js:344
    throw new Error('Can\'t set headers after they are sent.');
    Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:344:11)
    at ServerResponse.header (/Users/bounty/Projects/_learning/react-express/node_modules/express/lib/response.js:718:10)
at ServerResponse.send (/Users/bounty/Projects/_learning/react-express/node_modules/express/lib/response.js:163:12)
    at ServerResponse.json (/Users/bounty/Projects/_learning/react-express/node_modules/express/lib/response.js:249:15)
    at /Users/bounty/Projects/_learning/react-express/server/routes/posts.js:86:9
    at nextTickCallbackWith0Args (node.js:452:9)
    at process._tickCallback (node.js:381:13)

Here is my posts.js router:

module.exports = function(router) {

    var Post = require('../models/post.js');

    // middleware for the api requests
    router.use(function(req, res, next) {
        // do logging
        console.log('something is happening.');
        next(); // make sure we go to our next route and don't stop here
    });

    // test route to make sure everything is working (accessed at GET http://localhost:8080/api)

    router.get('/', function(req, res) {
        res.json({ message: 'hooray! welcome to our api!' });   
    });

    // all routes here

    // routes that end in /posts
    router.route('/posts')

        // create a Post (accessed at POST http://localhost:7777/api/posts)
        .post(function(req, res) {
            var post = new Post();
            post.postTitle = req.body.postTitle; // set the post name (comes from request) 

            // save post and check for errors
            post.save(function(err) {
                if (err)
                    res.send();

                res.json({ message: 'post created!' });
            });
        })

        // get all Posts (accessed at GET http://localhost:7777/api/posts)
        .get(function(req, res) {
            Post.find(function(err, posts) {
                if (err)
                    res.send();

                res.json(posts);
            });
        });

    // routes that end in /posts for specific id
    router.route('/posts/:post_id')

        // get the post with that id
        .get(function(req, res) {
            Post.findById(req.params.post_id, function(err, post) {
                if (err)
                    res.send(err);

                res.json(post);
            });
        })

        // update the post with that id
        .put(function(req, res) {
            Post.findById(req.params.post_id, function(err, post) {
                if (err)
                    res.send(err);

                post.postTitle = req.body.postTitle;

                // save the post
                post.save(function(err) {
                    if (err)
                        res.send(err);

                    res.json({ message: 'post updated!' });
                });
            });
        })

        // deletes the post with that id
        .delete(function(req, res) {
            Post.remove({
                _id: req.params.post_id
            }, function(err, post) {
                if (err) {
                    res.send(err);
                }
                res.json({ message: 'post deleted!' });
            });
        });
}
bounty
  • 409
  • 1
  • 4
  • 8
  • What does `post.save()` or `post.find()` actually do? It this your database? – jfriend00 Jan 25 '16 at 00:57
  • @jfriend00 Yes, save() puts a post into the database. Find() gets the posts from the database. The database is MongoDB. – bounty Jan 25 '16 at 02:23

7 Answers7

71

You need to add the 'return' so that you don't reply twice.

// save post and check for errors
post.save(function(err) {
    if (err) {
        return res.send();
    }
    res.json({ message: 'post created!' });
});
Alexis Tyler
  • 1,394
  • 6
  • 30
  • 48
Gadi
  • 1,152
  • 9
  • 6
  • Thanks! this answer solved part of the problem. I have been struggling with this for hours. – AllJs Jun 25 '16 at 06:52
  • When writing middleware, look out for multiple calls to next() – Michael Ribbons Nov 03 '16 at 00:14
  • Is this always the case? `res` statements must always be returned? – softcode Feb 22 '17 at 18:04
  • @softcode I think this is because if there is an error the `res.send()` will be called and then the `res.json({ message: 'post created!' });`. The `return` works as a breaker, please note that the `if` statement does not have `else` – Coyolero Mar 10 '17 at 16:45
  • you could alternatively use and if/else statement instead of an if statement – Isaac Pak Aug 05 '17 at 15:24
17

That particular error message is pretty much always caused because of a timing error in the handling of an async response that causes you to attempt to send data on a response after the response has already been sent.

It usually happens when people treat an async response inside an express route as a synchronous response and they end up sending data twice.


One place I see you would get this is in any of your error paths:

When you do this:

       // save post and check for errors
        post.save(function(err) {
            if (err)
                res.send();

            res.json({ message: 'post created!' });
        });

If post.save() generates an error, you will do res.send() and then you will do res.json(...) after it. Your code needs to have a return or an else so when there's an error you don't execute both code paths.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
6

So, this can happen in Express when attempting to send res.end twice which res.send and res.json both do. In your if(err) block you'll want to return res.send() as res.send runs asynchronously and res.json is getting called as well. I'm wondering if you're getting an error in your delete route? Hope this helps.

Best!

R.A. Lucas
  • 1,121
  • 1
  • 12
  • 17
4

You are using res.send() or res.json() twice in the same request

this send the headers first, followed by body of the response and then headers again. req.next is usually not a function, next is rather passed as a third argument of the middleware. Use that if you want to drop to the next middleware. (assuming you are using Express framework)

1

Just for the sake of completeness I will also mention that: Sometime problem may be in a the middleware you may be using by calling app.use.

After checking for obvious errors as mentioned in previous answers:

You should remove all the app.use statement then reintroduce them one by one, to find problematic module.

Seraj Ahmad
  • 405
  • 6
  • 10
0

For a quick fix you can just check res.finished before calling res.send():

if (!res.finished)
    res.send()
S01ds
  • 301
  • 5
  • 8
-1
    If you are using res.send() inside any loop, then you need to break it after the use of res.send(). So that it won't allow resetting of the res headers again and again. 
    for e.g : 
    for(){
if(){
res.send();
break;
}
else(){
res.send();
break;
}    
    }
In my case this is the problem and I solved it like this.
    Hope it may help someone in future.
    Thanks
Geek_KK
  • 192
  • 2
  • 11