Express.js error: Cannot set headers after they are sent to the client

Trouble

Your Express.js server is throwing an ERR_HTTP_HEADERS_SENT: Cannot set headers after they are sent to the client and you have a useless stack trace to work from like the following:

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:467:11)
    at ServerResponse.header (/usr/src/app/node_modules/express/lib/response.js:771:10)
    at ServerResponse.send (/usr/src/app/node_modules/express/lib/response.js:170:12)
    at done (/usr/src/app/node_modules/express/lib/response.js:1008:10)
    at render_file (/usr/src/app/node_modules/hbs/lib/hbs.js:49:14)
    at ReadFileContext.callback (/usr/src/app/node_modules/hbs/lib/hbs.js:168:16)
    at FSReqCallback.readFileAfterOpen [as oncomplete] (fs.js:232:13)

Why the heck do I get this?

As the cause of this issue is extremely well explained in "Understanding Node Error ERR_HTTP_HEADERS_SENT" by Prosper Opara I let you get the insights there.
I would just add to the article that the lack of using the return keyword when calling the Express next() function may also cause the ERR_HTTP_HEADERS_SENT issue.

Solution

By browsing the Express.js git repository I came across this issue that provides a very neat code snippet that improves the stack trace to identify the line responsible for the issue.

The code snippet consists of a middleware that monkey patches the res.render and res.send Express.js functions in order to prevent the application from crashing and improve the logged stack trace.

Here is the snippet:

app.use((req, res, next) => {
    const render = res.render;
    const send = res.send;
    res.render = function renderWrapper(...args) {
        Error.captureStackTrace(this);
        return render.apply(this, args);
    };
    res.send = function sendWrapper(...args) {
        try {
            send.apply(this, args);
        } catch (err) {
            console.error(`Error in res.send | ${err.code} | ${err.message} | ${res.stack}`);
        }
    };
    next();
});

If you plug this onto your Express.js application you will get a much more useful stack trace like below:

Error in res.send | ERR_HTTP_HEADERS_SENT | Cannot set headers after they are sent to the client | Error
     at ServerResponse.renderWrapper [as render] (/usr/src/app/app/server.js:289:15)
     at index (/usr/src/app/app/controllers/home-controller.js:88:21)
     at processTicksAndRejections (internal/process/task_queues.js:89:5)

In the above stack trace the line responsible for the second render is the line 88 of the home-controller.js file. Bingo!

All the credit should go to James Starkie a.k.a jstarmx on Github. James you made my day on this one ☀️.

NOTE: Do not forget to remote the middleware once you are done debugging 😉