Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA
Erro resolvidoIntermediáriocódigo testado

Cannot set headers after they are sent to the client

Por que o Express reclama de header depois da resposta enviada, as quatro situações que provocam o ERR_HTTP_HEADERS_SENT e a correção de cada uma.

Rodolfo Mori12 min de leitura

Cannot set headers after they are sent to the client significa que o código tentou alterar os cabeçalhos de uma resposta HTTP depois que o envio já havia começado. No Express, a causa mais comum é ter dois caminhos com res. para a mesma requisição: o primeiro responde e o segundo continua executando.

Imagine um envelope com endereço do lado de fora e documento dentro. Os headers são as informações externas; o body é o conteúdo. Depois que o envelope entra no caminhão, não dá para trocar o endereço. No Node, o primeiro res.write, res.send, res.json ou res.end pode confirmar os headers; tentar definir status ou cabeçalho depois disso dispara ERR_HTTP_HEADERS_SENT. Ainda é possível enviar novos pedaços de body em um streaming, mas não reescrever o que já foi confirmado.

Todos os exemplos aqui são da API do VetClub, uma clínica veterinária que agenda consultas. Tudo rodou no Node 24.16.0 com Express 5.2.1, e cada saída abaixo foi copiada do terminal. Cada causa é um arquivo separado, escutando na própria porta — por isso o número muda de um curl para o outro.

O stack trace e o código ERR_HTTP_HEADERS_SENT

Esta rota cadastra uma consulta. Ela valida o nome do pet, e é aí que o problema mora:

js
import express from 'express';

const app = express();
app.use(express.json());

const agenda = [];

app.post('/consultas', (req, res) => {
  const { pet, tutor, horario } = req.body;

  if (!pet) {
    res.status(400).json({ erro: 'informe o nome do pet' });
  }

  agenda.push({ pet, tutor, horario });
  res.status(201).json({ id: agenda.length, pet, horario });
});

app.listen(4571, () => console.log('VetClub ouvindo na porta 4571'));

Mandando um agendamento sem o nome do pet:

bash
curl -i -X POST http://localhost:4571/consultas \
  -H 'Content-Type: application/json' \
  -d '{"tutor":"Marina","horario":"14:30"}'
HTTP/1.1 400 Bad Request X-Powered-By: Express Content-Type: application/json; charset=utf-8 Content-Length: 32 ETag: W/"20-eDD7ZoZcf2R9gxCP9r8LMC7jEgk" Date: Sat, 22 Aug 2026 22:42:47 GMT Connection: keep-alive Keep-Alive: timeout=5

{“erro”:“informe o nome do pet”}

Do lado de quem chamou, está tudo certo: 400, mensagem clara, conexão fechada com educação. O estrago aparece no terminal do servidor:

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client at ServerResponse.setHeader (node:_http_outgoing:647:11) at ServerResponse.header (/private/tmp/vetclub/node_modules/express/lib/response.js:686:10) at ServerResponse.send (/private/tmp/vetclub/node_modules/express/lib/response.js:163:12) at ServerResponse.json (/private/tmp/vetclub/node_modules/express/lib/response.js:252:15) at file:///private/tmp/vetclub/causa1.js:16:19 at Layer.handleRequest (/private/tmp/vetclub/node_modules/router/lib/layer.js:152:17) at next (/private/tmp/vetclub/node_modules/router/lib/route.js:157:13) at Route.dispatch (/private/tmp/vetclub/node_modules/router/lib/route.js:117:3) at handle (/private/tmp/vetclub/node_modules/router/index.js:435:11) at Layer.handleRequest (/private/tmp/vetclub/node_modules/router/lib/layer.js:152:17)

São dez quadros porque dez é o limite padrão do Node, e essa forma é fixa — vale decorar. Os quatro primeiros at são sempre os mesmos: setHeaderheadersendjson. Eles são o caminho de dentro do Express e não interessam — nem eles nem os cinco de baixo, que são o roteador chamando a sua rota. O quadro que importa é o quinto:

text
at file:///private/tmp/vetclub/causa1.js:16:19

Essa é a segunda resposta, com arquivo, linha e coluna. No exemplo, linha 16 é o res.status(201).json(...). O que o trace não te conta é onde ficou a primeira — e é por isso que a maior parte do tempo de depuração vai embora procurando o outro res. na mesma rota.

O erro nem é do Express, aliás. Ele nasce no módulo http do Node:

js
import http from 'node:http';

const servidor = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
  res.write('Amora — ');
  res.write('vacinada\n'); // dois write no mesmo corpo: permitido
  res.end();

  try {
    res.setHeader('X-Clinica', 'VetClub'); // agora não dá mais
  } catch (erro) {
    console.log('name :', erro.name);
    console.log('code :', erro.code);
    console.log('msg  :', erro.message);
  }
});

servidor.listen(4580, () => console.log('http puro na porta 4580'));
name : Error code : ERR_HTTP_HEADERS_SENT msg : Cannot set headers after they are sent to the client

Repare em dois detalhes. Primeiro: err.code é ERR_HTTP_HEADERS_SENT, e é por esse código que você identifica o erro em produção, não pela mensagem em inglês. Segundo: os dois res.write seguidos passaram sem reclamação. O corpo pode sair em pedaços; o cabeçalho não pode voltar atrás.

a resposta sai nesta ordem — e não volta status + headers HTTP/1.1 400 … corpo o JSON fim (res.end) 2º res.json → setHeader ERR_HTTP_HEADERS_SENT

Causa 1 — o return que faltou depois do res.json()

Volte à rota do começo. res.status(400).json(...) responde, mas não interrompe a função. O JavaScript continua na linha seguinte, empurra a consulta sem pet para dentro da agenda e tenta responder 201.

A correção é uma palavra:

diff
   if (!pet) {
-    res.status(400).json({ erro: 'informe o nome do pet' });
+    return res.status(400).json({ erro: 'informe o nome do pet' });
   }

O return não tem nada a ver com o valor devolvido — o Express ignora o retorno do handler. Ele existe só para parar o resto da função. Com ele, os dois caminhos passam a funcionar:

bash
for corpo in '{"tutor":"Marina","horario":"14:30"}' \
             '{"pet":"Amora","tutor":"Marina","horario":"14:30"}'; do
  curl -s -w '  <- %{http_code}\n' -X POST http://localhost:4572/consultas \
    -H 'Content-Type: application/json' -d "$corpo"
done
{"erro":"informe o nome do pet"} <- 400 {"id":1,"pet":"Amora","horario":"14:30"} <- 201

Causa 2 — o next() chamado depois de já ter respondido

A clínica só deixa a recepção listar a agenda. O middleware abaixo verifica o perfil, devolve 403 para quem não é da recepção — e mesmo assim chama next():

js
function apenasRecepcao(req, res, next) {
  const perfil = req.get('X-Perfil');

  if (perfil !== 'recepcao') {
    res.status(403).json({ erro: 'somente a recepcao agenda consultas' });
  }

  next();
}

app.get('/consultas', apenasRecepcao, (req, res) => {
  res.json([{ id: 1, pet: 'Amora', horario: '14:30' }]);
});

Aqui o stack trace muda de cara, e a diferença é a peça que te salva:

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client at ServerResponse.setHeader (node:_http_outgoing:647:11) at ServerResponse.header (/private/tmp/vetclub/node_modules/express/lib/response.js:686:10) at ServerResponse.send (/private/tmp/vetclub/node_modules/express/lib/response.js:163:12) at ServerResponse.json (/private/tmp/vetclub/node_modules/express/lib/response.js:252:15) at file:///private/tmp/vetclub/causa2.js:16:7 at Layer.handleRequest (/private/tmp/vetclub/node_modules/router/lib/layer.js:152:17) at next (/private/tmp/vetclub/node_modules/router/lib/route.js:157:13) at apenasRecepcao (file:///private/tmp/vetclub/causa2.js:12:3) at Layer.handleRequest (/private/tmp/vetclub/node_modules/router/lib/layer.js:152:17) at next (/private/tmp/vetclub/node_modules/router/lib/route.js:157:13)

O quadro que denuncia a causa é o oitavo, o único com nome de função sua: at apenasRecepcao. Leia dali para cima — apenasRecepcao chamou next na linha 12, o next chamou o handler da linha 16, e o handler tentou responder de novo. Quando o nome de um middleware seu aparece no trace, ele é o culpado — respondeu e deixou a fila andar.

A correção é a mesma palavra da causa 1, no lugar certo:

js
function apenasRecepcao(req, res, next) {
  const perfil = req.get('X-Perfil');

  if (perfil !== 'recepcao') {
    return res.status(403).json({ erro: 'somente a recepcao agenda consultas' });
  }

  next();
}

Batendo na rota sem o cabeçalho e com ele, agora cada requisição recebe uma resposta só, e o log do servidor fica limpo:

{"erro":"somente a recepcao agenda consultas"} <- 403 [{"id":1,"pet":"Amora","horario":"14:30"}] <- 200

Se essa cadeia de next() ainda te parece nebulosa, vale voltar em middleware no Express antes de seguir.

Causa 3 — o callback assíncrono respondendo em segundo lugar

Esta é a única das quatro que derruba o processo, e a mais difícil de achar, porque as duas respostas moram em linhas que nunca rodam juntas.

A rota busca um prontuário e, para não deixar o app do balcão pendurado, monta um setTimeout de 150 ms que devolve 504. Só que a busca real leva 300 ms:

js
function buscarProntuario(id) {
  return new Promise((resolve) => {
    setTimeout(() => resolve({ id, pet: 'Amora', vacinas: 3 }), 300);
  });
}

app.get('/prontuarios/:id', (req, res) => {
  setTimeout(() => {
    res.status(504).json({ erro: 'o prontuario demorou demais' });
  }, 150);

  buscarProntuario(req.params.id).then((prontuario) => {
    res.json(prontuario);
  });
});

O cliente recebe o 504 normalmente. Cento e cinquenta milissegundos depois, o .then acorda e responde de novo:

node:_http_outgoing:647 throw new ERR_HTTP_HEADERS_SENT('set'); ^

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client at ServerResponse.setHeader (node:_http_outgoing:647:11) at ServerResponse.header (/private/tmp/vetclub/node_modules/express/lib/response.js:686:10) at ServerResponse.send (/private/tmp/vetclub/node_modules/express/lib/response.js:163:12) at ServerResponse.json (/private/tmp/vetclub/node_modules/express/lib/response.js:252:15) at file:///private/tmp/vetclub/causa3.js:17:9 { code: ‘ERR_HTTP_HEADERS_SENT’ }

Node.js v24.16.0

Note o throw na primeira linha e o Node.js v24.16.0 na última: isso não é um log, é o processo morrendo. A próxima requisição não encontra ninguém:

curl: (7) Failed to connect to localhost port 4574 after 1 ms: Couldn't connect to server

O motivo é estrutural. Nas causas 1 e 2 o segundo res.json acontece dentro da cadeia do Express, que envolve tudo num tratamento de erro. Aqui ele acontece dentro de um callback que o event loop chamou depois, longe do handler. Não existe ninguém para pegar: vira rejeição não tratada, que no Node 24 é promovida a exceção não capturada, e o processo encerra.

A correção tem duas partes: cancelar o relógio quando a busca chega, e conferir se alguém já respondeu.

js
app.get('/prontuarios/:id', (req, res) => {
  const relogio = setTimeout(() => {
    res.status(504).json({ erro: 'o prontuario demorou demais' });
  }, 150);

  buscarProntuario(req.params.id).then((prontuario) => {
    clearTimeout(relogio);
    if (res.headersSent) return;
    res.json(prontuario);
  });
});

Com isso, a requisição lenta continua devolvendo 504 — e o servidor continua de pé para a próxima. Sempre que uma Promise disputa a resposta com um timer, as duas travas precisam existir.

Causa 4 — o middleware de erro respondendo por cima do handler

O VetClub tem um tratador de erro central, como toda API séria deveria ter. A rota busca a consulta pelo id e loga o pet entregue:

js
const agenda = [{ id: 1, pet: 'Amora', tutor: 'Marina', horario: '14:30' }];

app.get('/consultas/:id', (req, res) => {
  const consulta = agenda.find((c) => c.id === Number(req.params.id));

  res.json(consulta);
  console.log(`consulta de ${consulta.pet} entregue`);
});

app.use((err, req, res, next) => {
  console.error('[erro no handler]', err.message);
  res.status(500).json({ erro: 'falha interna na clinica' });
});

Pedindo um id que não existe com curl -i http://localhost:4575/consultas/99, o cliente recebe isto:

HTTP/1.1 200 OK X-Powered-By: Express Content-Type: application/json; charset=utf-8 Date: Sat, 22 Aug 2026 22:44:02 GMT Connection: keep-alive Keep-Alive: timeout=5 Content-Length: 0

200 com corpo vazio. O find não achou nada, res.json(undefined) é uma resposta válida e ela saiu. Só depois a linha do console.log explodiu — é o velho Cannot read properties of undefined — e o middleware de erro tentou mandar um 500 que já não cabia:

[erro no handler] Cannot read properties of undefined (reading 'pet') Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client at ServerResponse.setHeader (node:_http_outgoing:647:11) at ServerResponse.header (/private/tmp/vetclub/node_modules/express/lib/response.js:686:10) at ServerResponse.send (/private/tmp/vetclub/node_modules/express/lib/response.js:163:12) at ServerResponse.json (/private/tmp/vetclub/node_modules/express/lib/response.js:252:15) at file:///private/tmp/vetclub/causa4.js:16:19 at Layer.handleError (/private/tmp/vetclub/node_modules/router/lib/layer.js:116:17) at trimPrefix (/private/tmp/vetclub/node_modules/router/index.js:340:13) at /private/tmp/vetclub/node_modules/router/index.js:297:9 at processParams (/private/tmp/vetclub/node_modules/router/index.js:582:12) at next (/private/tmp/vetclub/node_modules/router/index.js:291:5)

A assinatura desta causa é o quadro at Layer.handleError, logo abaixo do seu arquivo. Nas causas 1 e 2 ali estava Layer.handleRequest. handleError significa que a segunda resposta partiu do seu middleware de erro — e que existe um erro de verdade escondido atrás do ruído. Vale ler tratamento de erro centralizado no Express para montar esse handler direito.

res.headersSent: o guarda que evita o segundo envio

res.headersSent é um booleano que o Node mantém: false antes do primeiro byte sair, true depois. É a pergunta que todo middleware de erro precisa fazer antes de responder.

js
app.get('/consultas/:id', (req, res) => {
  const consulta = agenda.find((c) => c.id === Number(req.params.id));

  console.log('antes do res.json  ->', res.headersSent);
  res.json(consulta);
  console.log('depois do res.json ->', res.headersSent);

  console.log(`consulta de ${consulta.pet} entregue`);
});

app.use((err, req, res, next) => {
  console.error('[erro]', err.message);

  if (res.headersSent) {
    console.error('[erro] resposta ja enviada, nao respondo de novo');
    return next(err);
  }

  res.status(500).json({ erro: 'falha interna na clinica' });
});
antes do res.json -> false depois do res.json -> true [erro] Cannot read properties of undefined (reading 'pet') [erro] resposta ja enviada, nao respondo de novo TypeError: Cannot read properties of undefined (reading 'pet') at file:///private/tmp/vetclub/guarda.js:14:39 at Layer.handleRequest (/private/tmp/vetclub/node_modules/router/lib/layer.js:152:17) at next (/private/tmp/vetclub/node_modules/router/lib/route.js:157:13) at Route.dispatch (/private/tmp/vetclub/node_modules/router/lib/route.js:117:3) at handle (/private/tmp/vetclub/node_modules/router/index.js:435:11) at Layer.handleRequest (/private/tmp/vetclub/node_modules/router/lib/layer.js:152:17) at /private/tmp/vetclub/node_modules/router/index.js:295:15 at param (/private/tmp/vetclub/node_modules/router/index.js:600:14) at param (/private/tmp/vetclub/node_modules/router/index.js:610:14) at processParams (/private/tmp/vetclub/node_modules/router/index.js:664:3)

Compare com a saída da causa 4. O ERR_HTTP_HEADERS_SENT sumiu, e o que sobrou no log foi o TypeError — o bug de verdade, na primeira linha e com o número exato. Era ele que o ruído estava escondendo.

Três causas sujam o log, uma derruba o servidor

Vale saber com o que você está lidando, porque a urgência muda. Bati cinco vezes seguidas na rota quebrada da causa 1, com o servidor rodando em segundo plano e a saída dele redirecionada para log1.txt:

bash
for i in 1 2 3 4 5; do
  curl -s -o /dev/null -w "%{http_code} " -X POST http://localhost:4571/consultas \
    -H 'Content-Type: application/json' -d '{"tutor":"Marina"}'
done
echo
pgrep -f "node causa1.js" > /dev/null && echo "servidor ainda de pe"
grep -c "ERR_HTTP_HEADERS_SENT" log1.txt
400 400 400 400 400 servidor ainda de pe 5

Cinco respostas 400 corretas, servidor vivo, e cinco blocos de stack trace acumulados no log — um por requisição. Do lado de fora, ninguém percebe. Do lado de dentro, o seu log de produção vira lixo e o alerta que importa passa despercebido no meio.

A causa 3 é de outra natureza: o processo morreu na primeira requisição, e o curl seguinte nem conseguiu conectar. Em produção, com um orquestrador reiniciando o container, isso vira reinício em loop.

Só que o critério não é “síncrono derruba o log, assíncrono derruba o servidor”. O que decide é se o Express ainda tem a sua função na mão quando o erro estoura. Troque o .then da causa 3 por await, sem mexer em mais nada:

js
app.get('/prontuarios/:id', async (req, res) => {
  setTimeout(() => {
    res.status(504).json({ erro: 'o prontuario demorou demais' });
  }, 150);

  const prontuario = await buscarProntuario(req.params.id);
  res.json(prontuario);
});

Duas requisições seguidas nessa rota, com o mesmo erro acontecendo nas duas:

bash
node causa3-await.js > log-await.txt 2>&1 &
sleep 1
curl -s -o /dev/null -w '1a requisicao: %{http_code}\n' http://localhost:4581/prontuarios/1
sleep 0.5
curl -s -o /dev/null -w '2a requisicao: %{http_code}\n' http://localhost:4581/prontuarios/1
sleep 0.5
pgrep -f "node causa3-await.js" > /dev/null && echo "servidor ainda de pe"
1a requisicao: 504 2a requisicao: 504 servidor ainda de pe

O processo não morre. Com await, o res.json atrasado ainda está dentro da função async, então o erro rejeita a promise que o handler devolveu — e o Express 5 trata essa rejeição como trataria um throw comum. No log-await.txt fica este bloco, repetido uma vez por requisição:

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client at ServerResponse.setHeader (node:_http_outgoing:647:11) at ServerResponse.header (/private/tmp/vetclub/node_modules/express/lib/response.js:686:10) at ServerResponse.send (/private/tmp/vetclub/node_modules/express/lib/response.js:163:12) at ServerResponse.json (/private/tmp/vetclub/node_modules/express/lib/response.js:252:15) at file:///private/tmp/vetclub/causa3-await.js:17:7

Com .then, não: quando o callback roda, a função do handler já terminou e devolveu undefined faz tempo. A rejeição nasce órfã e o processo cai. O critério prático, então: res. depois de um await, dentro do handler async, é log sujo e você tem tempo. res. dentro de um callback que o Express nunca recebeu de volta — setTimeout, .then, callback de driver de banco, listener de evento — é incidente.

Roteiro: achar o segundo res. dentro do seu handler

O stack trace mostra a segunda resposta e esconde a primeira. Este middleware resolve isso: ele troca o res.json por uma versão que guarda de onde veio cada envio e denuncia o segundo, sem deixar ele sair.

js
function deOndeVeio() {
  return new Error().stack.split('\n')[3].trim();
}

app.use((req, res, next) => {
  const jsonOriginal = res.json.bind(res);
  let primeira = null;

  res.json = (corpo) => {
    if (primeira) {
      console.error(`[2x] ${req.method} ${req.originalUrl}`);
      console.error(`     1a resposta: ${primeira}`);
      console.error(`     2a resposta: ${deOndeVeio()}`);
      return res;
    }
    primeira = deOndeVeio();
    return jsonOriginal(corpo);
  };

  next();
});

Com ele ligado na frente da rota quebrada da causa 1 — onde, no arquivo completo, o res.status(400) cai na linha 34 e o res.status(201) na 38:

[2x] POST /consultas 1a resposta: at file:///private/tmp/vetclub/rastreador.js:34:21 2a resposta: at file:///private/tmp/vetclub/rastreador.js:38:19

Duas linhas, os dois culpados, com arquivo e número. É o que faltava no stack trace original. E ele não para na sua pasta: quando a primeira resposta sai de uma dependência, o quadro capturado vem com o caminho dentro de node_modules, arquivo e linha da biblioteca.

O limite dele é o método. Esse rastreador cobre res.json, e só. Um middleware que responde com res.send ou res.end passa direto pela troca e o ERR_HTTP_HEADERS_SENT volta a estourar — se a suspeita é de terceiro, repita a mesma troca nesses dois métodos. O roteiro de uso é curto: coloque o middleware antes de todas as rotas, reproduza a requisição problemática, anote as duas linhas e tire ele do projeto. Em produção, ele mascara o defeito em vez de corrigir.

O mesmo roteiro, resumido, quando você não quiser colar código nenhum:

  1. Copie o quinto quadro at do stack trace — é o arquivo e a linha da segunda resposta.
  2. Abra a função e conte os res. dela. Quase sempre há dois.
  3. Olhe o quadro logo abaixo: Layer.handleRequest aponta para middleware ou rota; Layer.handleError aponta para o seu tratador de erro.
  4. Se não achar nenhum par óbvio, procure por res. depois de um await e dentro de setTimeout, .then, try/catch e callbacks de banco.

O próximo passo

Corrigido o erro, o hábito que evita a reincidência é estrutural: uma rota Express deveria ter um único ponto de saída por caminho, e todo res. que não é a última linha começa com return. Quando a API cresce e um arquivo passa a ter dez rotas, essa disciplina fica difícil de manter na base do olho — é a hora de quebrar em roteadores modulares por recurso e deixar cada arquivo pequeno o bastante para caber na tela. A trilha de Node segue daí para validação de entrada e autenticação, onde middlewares que respondem cedo são justamente a regra.

Como teste final, faça duas chamadas à rota corrigida: uma sem pet, que deve responder uma única vez com 400, e outra válida, que deve responder uma única vez com 201. Conte um log imediatamente antes de cada res.. Se uma chamada produzir dois logs de envio ou aparecer ERR_HTTP_HEADERS_SENT, ainda existe um caminho sem return ou um callback respondendo tarde.

Prefere aprender em vídeo?

Tem uma aula sobre este assunto no nosso canal.

Ver todos os vídeos do canal
  • express
  • erro
  • headers sent
  • node
  • middleware

Perguntas frequentes

Esse erro é do Express ou do Node?
Do Node. Quem lança é o módulo http, no método setHeader da resposta. Qualquer código em cima dele pode disparar o mesmo erro — http puro, Fastify, Koa. No Express ele aparece mais porque a cadeia de middlewares cria mais oportunidades de responder duas vezes.
Posso enviar o corpo em pedaços sem cair nesse erro?
Pode. Vários res.write seguidos são legítimos, e é assim que streaming e Server-Sent Events funcionam. O que não pode é mexer em cabeçalho depois que o primeiro pedaço saiu — a proibição é sobre header, não sobre corpo.
E se a segunda resposta vier de uma biblioteca de terceiros?
Acontece com middleware de CORS, de compressão ou de sessão mal encaixado na ordem. O rastreador do fim deste artigo aponta o arquivo e a linha mesmo quando o envio parte de dentro do node_modules — mas ele só cobre res.json. Se a biblioteca responde com res.send ou res.end, aplique a mesma troca nesses métodos também.
Dá para envolver o res.json num try/catch e seguir a vida?
Dá, e é a pior saída. O try/catch cala o aviso e mantém o defeito: sua rota continua com dois caminhos de resposta, e um deles nunca chega ao cliente. Corrija o fluxo e use res.headersSent apenas como rede de segurança.
Por que o cliente recebeu 200 com corpo vazio em vez do erro?
Porque res.json(undefined) é uma resposta válida: sai 200 com Content-Length 0. O 500 que o middleware de erro tentou mandar depois já não tinha para onde ir. Verifique se o registro existe antes de responder.

Dúvidas e comentários

Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.

Todo o código deste artigo foi executado em Node 24.16.0 com Express 5.2.1, e as saídas exibidas são as reais — como produzimos este conteúdo.

Fontes consultadas

  1. Node.js — ERR_HTTP_HEADERS_SENT — nodejs.org
  2. Express 5 — res.headersSent — expressjs.com
  3. MDN — Mensagens HTTP — developer.mozilla.org

Continue por aqui