Express.js error: Cannot set headers after they are sent to the client
Posted on
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 😉