'Cannot set headers after they are sent' — stop the double response
Quick answer: ERR_HTTP_HEADERS_SENT means your handler sent a response and then tried to respond again - a second res.send/res.json, or a header set after the body went out. Once a response is flushed it is committed. The fix is one response per request: return every res.* call and never next() after responding.
What the error looks like
It fires at runtime, on the request that responded twice:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (node:_http_outgoing:659:11)
at ServerResponse.header (.../express/lib/response.js:794:10)
at ServerResponse.send (.../express/lib/response.js:170:12)
The stack points at the second res.send/setHeader. The response was already flushed; the headers cannot change now.
Why it happens
Missing return after a response
res.send runs, code keeps going, and a later res.json responds again.
next() after responding
You sent a response and still called next(), so the next middleware or error handler responds too.
Two branches both respond
An if/else or a callback path and a fall-through that each send a response.
Async race
A timeout or error already responded, then a slow promise resolves and responds again.
Find it in three steps
Read the stack for the second send
# The top frames name the res.* call that ran when headers were already sent.
# That is the SECOND response - the bug is that the first one did not stop execution.Hunt for a response without return
// BAD - no return, so res.json runs too:
if (!user) res.status(404).send('not found');
res.json(user);Check error handlers and callbacks
// guard against responding when a response already went out
app.use((err, req, res, next) => {
if (res.headersSent) return next(err);
res.status(500).json({ error: 'internal' });
});Respond exactly once per request
Make every code path send one response and stop. The simplest discipline is to return every res.* call, so nothing falls through, and never call next() after you have responded.
// GOOD - return on every response, one path responds
app.get('/u/:id', async (req, res) => {
const user = await find(req.params.id);
if (!user) return res.status(404).send('not found');
return res.json(user);
});
For async work, make sure a timeout/error path and the success path cannot both fire - track whether you have responded, or use a framework that does (e.g. return the value in a handler that auto-responds).
See the request that misbehaved
Double-response bugs are hard to reproduce because they depend on a specific path or race. On your own servers, Infraveil traces requests through your backend, so the offending request shows up with its route and context in one view - and recovery stays approval-gated and recorded, on infrastructure you control.
Frequently asked questions
What causes 'Cannot set headers after they are sent'?
Your code sent a response and then tried to respond again - a second res.send/res.json, or a header set after the body went out. The response is already flushed, so the second attempt throws ERR_HTTP_HEADERS_SENT.
How do I find where it responds twice?
The stack trace points to the second res.* call. Look for a res.send/res.json without a return, an if/else where both branches respond, or next() called after a response.
Does this crash the whole server?
It throws for that one request. A global error handler contains it rather than crashing the process, but the client may get a wrong or truncated response - it is a real bug to fix, not just noise.
How do I prevent it?
Respond exactly once: return every res.* call, never call next() after responding, and guard error handlers with if (res.headersSent) return next(err).
Client-side, no signup — they run in your browser.