Error: no se pueden configurar encabezados después de enviarlos al cliente

Resuelto DjangoRocks asked hace 13 años • 49 respuestas

Soy bastante nuevo en Node.js y tengo algunos problemas.

Estoy usando Node.js 4.10 y Express 2.4.3.

Cuando intento acceder a http://127.0.0.1:8888/auth/facebook , seré redirigido a http://127.0.0.1:8888/auth/facebook_callback .

Luego recibí el siguiente error:

Error: Can't render headers after they are sent to the client.
    at ServerResponse.<anonymous> (http.js:573:11)
    at ServerResponse._renderHeaders (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:64:25)
    at ServerResponse.writeHead (http.js:813:20)
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/auth.strategies/facebook.js:28:15
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:113:13
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/strategyExecutor.js:45:39)
    at [object Object].pass (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/authExecutionScope.js:32:3)
    at [object Object].halt (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/authExecutionScope.js:29:8)
    at [object Object].redirect (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/authExecutionScope.js:16:8)
    at [object Object].<anonymous> (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/auth.strategies/facebook.js:77:15)
Error: Can't set headers after they are sent.
    at ServerResponse.<anonymous> (http.js:527:11)
    at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:195:11)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:150:23)
    at param (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:189:13)
    at pass (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:191:10)
    at Object.router [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:197:6)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15)
    at Object.auth [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:153:7)
Error: Can't set headers after they are sent.
    at ServerResponse.<anonymous> (http.js:527:11)
    at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:207:9)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:150:23)
    at param (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:189:13)
    at pass (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:191:10)
    at Object.router [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:197:6)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15)
    at Object.auth [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:153:7)
Error: Can't set headers after they are sent.
    at ServerResponse.<anonymous> (http.js:527:11)
    at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:150:23)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:207:9)
    at Object.auth [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:153:7)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15)
    at HTTPServer.handle (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:211:3)
    at Object.handle (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:105:14)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15)
Error: Can't set headers after they are sent.
    at ServerResponse.<anonymous> (http.js:527:11)
    at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:150:23)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:207:9)
    at HTTPServer.handle (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:211:3)
    at Object.handle (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:105:14)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15)
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session.js:323:9
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session.js:338:9

node.js:134
        throw e; // process.nextTick error, or 'error' event on first tick
        ^
Error: Can't set headers after they are sent.
    at ServerResponse.<anonymous> (http.js:527:11)
    at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13)
    at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:207:9)
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session.js:323:9
    at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session.js:338:9
    at Array.<anonymous> (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session/memory.js:57:7)
    at EventEmitter._tickCallback (node.js:126:26)

El siguiente es mi código:

var fbId= "XXX";
var fbSecret= "XXXXXX";
var fbCallbackAddress= "http://127.0.0.1:8888/auth/facebook_callback"

var cookieSecret = "node";     // enter a random hash for security

var express= require('express');
var auth = require('connect-auth')
var app = express.createServer();


app.configure(function(){
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(express.cookieParser());
    app.use(express.session({secret: cookieSecret}));
    app.use(auth([
        auth.Facebook({
            appId : fbId,
            appSecret: fbSecret,
            callback: fbCallbackAddress,
            scope: 'offline_access,email,user_about_me,user_activities,manage_pages,publish_stream',
            failedUri: '/noauth'
        })
    ]));
    app.use(app.router);
});


app.get('/auth/facebook', function(req, res) {
  req.authenticate("facebook", function(error, authenticated) {
    if (authenticated) {
      res.redirect("/great");
      console.log("ok cool.");
      console.log(res['req']['session']);
    }
  });
});

app.get('/noauth', function(req, res) {
  console.log('Authentication Failed');
  res.send('Authentication Failed');
});

app.get('/great', function( req, res) {
  res.send('Supercoolstuff');
});

app.listen(8888);

¿Puedo saber qué está mal con mi código?

DjangoRocks avatar Aug 12 '11 22:08 DjangoRocks
Aceptado

El resobjeto en Express es una subclase de Node.jshttp.ServerResponse ( lea la fuente http.js ). Puedes llamar res.setHeader(name, value)tantas veces como quieras hasta que llames res.writeHead(statusCode). Después writeHead, los encabezados están integrados y sólo puedes llamar res.write(data), y finalmente res.end(data).

El error "Error: no se pueden configurar los encabezados después de enviarlos". significa que ya estás en el estado Cuerpo o Finalizado, pero alguna función intentó establecer un encabezado o código de estado. Cuando vea este error, intente buscar cualquier cosa que intente enviar un encabezado después de que ya se haya escrito parte del cuerpo. Por ejemplo, busque devoluciones de llamada que se llamen accidentalmente dos veces o cualquier error que ocurra después de enviar el cuerpo.

En su caso, llamó a res.redirect(), lo que provocó que la respuesta finalizara. Entonces su código arrojó un error ( res.reqes null). y dado que el error ocurrió dentro de su devolución de llamada real function(req, res, next)(no dentro de una devolución de llamada), Connect pudo detectarlo y luego intentó enviar una página de error 500. Pero como los encabezados ya se enviaron, Node.js setHeaderarrojó el error que vio.

Lista completa de métodos de respuesta de Node.js/Express y cuándo deben llamarse:

La respuesta debe estar en Head y permanece en Head :

  1. res.writeContinue()
  2. res.statusCode = 404
  3. res.setHeader(name, value)
  4. res.getHeader(name)
  5. res.removeHeader(name)
  6. res.header(key[, val])(Solo exprés)
  7. res.charset = 'utf-8'(Solo Express; solo afecta a los métodos específicos de Express)
  8. res.contentType(type)(Solo exprés)

La respuesta debe estar en Cabeza y se convierte en Cuerpo :

  1. res.writeHead(statusCode, [reasonPhrase], [headers])

La respuesta puede estar en Head/Body y permanece en Body :

  1. res.write(chunk, encoding='utf8')

La respuesta puede estar en Head/Body y pasa a ser Finalizada :

  1. res.end([data], [encoding])

La respuesta puede estar en Cabeza/Cuerpo y permanece en su estado actual:

  1. res.addTrailers(headers)

La respuesta debe estar en Head y pasa a ser Finished :

  1. return next([err])(Solo Conexión/Express)
  2. Cualquier excepción dentro del middleware function(req, res, next)(solo Connect/Express)
  3. res.send(body|status[, headers|status[, status]])(Solo exprés)
  4. res.attachment(filename)(Solo exprés)
  5. res.sendfile(path[, options[, callback]])(Solo exprés)
  6. res.json(obj[, headers|status[, status]])(Solo exprés)
  7. res.redirect(url[, status])(Solo exprés)
  8. res.cookie(name, val[, options])(Solo exprés)
  9. res.clearCookie(name[, options])(Solo exprés)
  10. res.render(view[, options[, fn]])(Solo exprés)
  11. res.partial(view[, options])(Solo exprés)
yonran avatar Aug 17 '2011 00:08 yonran

Algunas de las respuestas de estas preguntas y respuestas son incorrectas. La respuesta aceptada tampoco es muy "práctica", por lo que quiero publicar una respuesta que explique las cosas en términos más simples. Mi respuesta cubrirá el 99% de los errores que veo publicados una y otra vez. Para conocer las razones reales detrás del error, consulte la respuesta aceptada.


HTTP utiliza un ciclo que requiere una respuesta por solicitud. Cuando el cliente envía una solicitud (por ejemplo, POST o GET), el servidor solo debe enviarle una respuesta.

Este mensaje de error:

Error: no se pueden configurar los encabezados después de enviarlos.

Suele ocurrir cuando envías varias respuestas para una solicitud. Asegúrese de que las siguientes funciones se llamen solo una vez por solicitud:

  • res.json()
  • res.send()
  • res.redirect()
  • res.render()

(y algunos más que rara vez se usan, verifique la respuesta aceptada)

La devolución de llamada de ruta no volverá cuando se llamen estas funciones res. Continuará ejecutándose hasta que llegue al final de la función o una declaración de devolución. Si deseas regresar al enviar una respuesta puedes hacerlo así: return res.send().


Tomemos, por ejemplo, este código:

app.post('/api/route1', function(req, res) {
  console.log('this ran');
  res.status(200).json({ message: 'ok' });
  console.log('this ran too');
  res.status(200).json({ message: 'ok' });
}

Cuando se envía una solicitud POST a /api/route1, se ejecutarán todas las líneas de la devolución de llamada. Se generará un mensaje de error No se pueden configurar los encabezados después de enviarlos porque res.json()se llama dos veces, lo que significa que se envían dos respuestas.

¡Solo se puede enviar una respuesta por solicitud!


El error en el ejemplo de código anterior era obvio. Un problema más típico es cuando tienes varias ramas:

app.get('/api/company/:companyId', function(req, res) {
  const { companyId } = req.params;
  Company.findById(companyId).exec((err, company) => {
      if (err) {
        res.status(500).json(err);
      } else if (!company) {
        res.status(404).json();      // This runs.
      }
      res.status(200).json(company); // This runs as well.
    });
}

Esta ruta con devolución de llamada adjunta encuentra una empresa en una base de datos. Al realizar una consulta para una empresa que no existe, ingresaremos a la else ifsucursal y enviaremos una respuesta 404. Después de eso, continuaremos con la siguiente declaración que también envía una respuesta. Ahora hemos enviado dos respuestas y aparecerá el mensaje de error. Podemos corregir este código asegurándonos de enviar solo una respuesta:

.exec((err, company) => {
  if (err) {
    res.status(500).json(err);
  } else if (!company) {
    res.status(404).json();         // Only this runs.
  } else {
    res.status(200).json(company);
  }
});

o regresando cuando se envía la respuesta:

.exec((err, company) => {
  if (err) {
    return res.status(500).json(err);
  } else if (!company) {
    return res.status(404).json();  // Only this runs.
  }
  return res.status(200).json(company);
});

Un gran pecador son las funciones asincrónicas. Tome la función de esta pregunta, por ejemplo:

article.save(function(err, doc1) {
  if (err) {
    res.send(err);
  } else {
    User.findOneAndUpdate({ _id: req.user._id }, { $push: { article: doc._id } })
    .exec(function(err, doc2) {
      if (err) res.send(err);
      else     res.json(doc2);  // Will be called second.
    })

    res.json(doc1);             // Will be called first.
  }
});

Aquí tenemos una función asincrónica ( findOneAndUpdate()) en el ejemplo de código. Si no hay errores se llamará ( err) . findOneAndUpdate()Debido a que esta función es asincrónica, se res.json(doc1)llamará inmediatamente. Supongamos que no hay errores en findOneAndUpdate(). Entonces se llamará al res.json(doc2)testamento . elseSe han enviado dos respuestas y aparece el mensaje de error No se pueden establecer encabezados .

La solución, en este caso, sería eliminar el archivo res.json(doc1). Para enviar ambos documentos de vuelta al cliente, res.json()el resto podría escribirse como res.json({ article: doc1, user: doc2 }).

Mika Sundland avatar Jan 06 '2018 01:01 Mika Sundland

También me encontré con este error por un tiempo. Creo (espero) haberlo comprendido y quería escribirlo aquí como referencia.

Cuando agrega middleware a connect o express (que se basa en connect) usando el app.usemétodo, está agregando elementos a Server.prototype.stackin connect (al menos con el actual npm install connect, que se ve bastante diferente al de github en esta publicación). Cuando el servidor recibe una solicitud, itera sobre la pila y llama al (request, response, next)método.

El problema es que, si en uno de los elementos del middleware se escribe en el cuerpo de la respuesta o en los encabezados (parece que es una cosa o la otra por alguna razón), pero no llama response.end()y usted llamanext() cuando Server.prototype.handlese completa el método principal, se dará cuenta. eso:

  1. no hay más elementos en la pila, y/o
  2. eso response.headerSentes verdad.

Entonces, arroja un error. Pero el error que arroja es solo esta respuesta básica (del http.jscódigo fuente de conexión:

res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end('Cannot ' + req.method + ' ' + req.url);

Allí mismo, está llamando res.setHeader('Content-Type', 'text/plain');, que probablemente haya configurado en su rendermétodo, sin llamar a Response.end() , algo como:

response.setHeader("Content-Type", "text/html");
response.write("<p>Hello World</p>");

La forma en que todo debe estructurarse es la siguiente:

Buen middleware

// middleware that does not modify the response body
var doesNotModifyBody = function(request, response, next) {
  request.params = {
    a: "b"
  };
  // calls next because it hasn't modified the header
  next();
};

// middleware that modify the response body
var doesModifyBody = function(request, response, next) {
  response.setHeader("Content-Type", "text/html");
  response.write("<p>Hello World</p>");
  response.end();
  // doesn't call next()
};

app.use(doesNotModifyBody);
app.use(doesModifyBody);

Middleware problemático

var problemMiddleware = function(request, response, next) {
  response.setHeader("Content-Type", "text/html");
  response.write("<p>Hello World</p>");
  next();
};

El middleware problemático establece el encabezado de respuesta sin llamar response.end()ni llamar next(), lo que confunde al servidor de Connect.

Lance avatar Oct 17 '2011 03:10 Lance

Tuve el mismo problema y me di cuenta de que era porque estaba llamando res.redirectsin una returndeclaración, por lo que la nextfunción también se llamaba inmediatamente después:

auth.annonymousOnly = function(req, res, next) {
    if (req.user) res.redirect('/');
    next();
};

Que debería haber sido:

auth.annonymousOnly = function(req, res, next) {
    if (req.user) return res.redirect('/');
    next();
};
ergusto avatar Feb 14 '2015 13:02 ergusto