Cannot Set Headers After They Are Sent to the Client: How to Fix It
"Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client" means your Express or Node.js code tried to send a second response to one request. The common causes — a missing return, callbacks and loops that respond twice, next() after res.send(), async errors — and how to fix each.
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
Every HTTP request gets exactly one response. This error means your Node.js code (usually Express) tried to respond twice to the same request. The first response went out fine; the second attempt crashed.
The fix is always the same idea: make sure each code path sends one response and then stops.
Cause 1: Missing return (the big one)
app.get('/user/:id', async (req, res) => {
const user = await db.findUser(req.params.id)
if (!user) {
res.status(404).json({ error: 'Not found' }) // sends a response...
}
res.json(user) // ...then sends another 💥
})
res.json() sends the response but doesn't stop your function. Add return:
if (!user) {
return res.status(404).json({ error: 'Not found' })
}
return res.json(user)
Writing return res... everywhere is a good habit.
Cause 2: Responding inside a loop
for (const item of items) {
if (item.invalid) res.status(400).send('Invalid item') // may run many times
}
res.send('OK')
Collect the result first, then respond once:
const invalid = items.find(item => item.invalid)
if (invalid) return res.status(400).send('Invalid item')
return res.send('OK')
Cause 3: Calling next() after responding
In middleware, either respond or call next() — not both:
function requireAuth(req, res, next) {
if (!req.user) {
return res.status(401).json({ error: 'Login required' })
}
next()
}
Without that return, the request continues to the route handler, which responds again.
Cause 4: Callbacks and events that fire more than once
Old-style callbacks, stream 'data' events, or an on('error') plus a success callback can each try to respond. Make sure only one path ends the request, or track it with if (res.headersSent) return.
Cause 5: Async errors after a response
res.json({ ok: true })
await sendWelcomeEmail(user) // throws...
// ...and your error handler tries to send a 500 too
If work continues after you respond, the error handler can't respond again. Either do the work before responding, or move it to a background job and catch its errors there. (Background Jobs and Cron)
Express 5 forwards rejected promises from async handlers to your error handler automatically, which makes these double responses show up more clearly. Your error handler should check first:
app.use((err, req, res, next) => {
if (res.headersSent) return next(err)
res.status(500).json({ error: 'Something went wrong' })
})
Cause 6: Setting a header after writing the body
res.setHeader(), res.cookie() and res.status() must come before res.send()/res.json()/res.write(). Once the body starts, headers are locked. (HTTP Headers Explained)
The summary
- One request, one response. This error means you sent two.
- Add
returnbefore everyres.send/res.json, respond once outside loops, and don't callnext()after responding. - Check
res.headersSentin error handlers.
EasySpawn runs your Express app and its logs on the same server as Claude Code, so it can trace which code path answered twice and fix it. See how it works or join the waitlist.
Related: What Is Express? · Deploy an Express App · async/await in JavaScript · 500 Internal Server Error
Keep reading
429 Too Many Requests: What It Means and How to Fix It
A 429 Too Many Requests response means you've hit a rate limit: too many requests in too short a time. How to read the Retry-After and rate-limit headers, how to retry properly with backoff, the usual causes (loops, missing caching, shared IPs, AI API limits), and how to add rate limits to your own app.
HTTP Headers Explained: The Hidden Part of Every Request
Every web request and response carries headers — extra information about the content, the client, authentication, caching and security. The headers you'll meet most (Content-Type, Authorization, Cookie, Cache-Control, CORS), how to see them in DevTools, and how to set them.