Node.js "write after end" error

francojposa picture francojposa · Sep 1, 2016 · Viewed 19.2k times · Source

Starting off with a basic node app, and I can't figure out how to beat this "write after end" error, even with the callbacks suggested on several sites.

index.js:

var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");

var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;

server.start(router.route, handle);

server.js:

var http = require("http");
var url = require("url");

function start(route, handle) {
  function onRequest(request, response) {
    var postData ="";
    var pathname = url.parse(request.url).pathname;
    console.log("Request for " + pathname + " received.");

    route(handle, pathname, response);
   }

  http.createServer(onRequest).listen(8888);
  console.log("Server has started.");
}

exports.start = start;

router.js:

function route(handle, pathname, response) {
  console.log("About to route a request for " + pathname);
  if (typeof handle[pathname] === 'function') {
    handle[pathname](response);
  } 
  else {
    console.log("No request handler found for " + pathname);
    response.writeHead(404, {"Content-Type": "text/plain"});
    response.write("404 Not found");
    response.end();
  }
}

exports.route = route;

and now the culprit, requestHandlers.js:

function start(response) {
  console.log("Request handler 'start' was called.");

  var body = '<html>'+
    '<head>'+
    '<meta http-equiv="Content-Type" content="text/html; '+
    'charset=UTF-8" />'+
    '</head>'+
    '<body>'+
    '<div>Hello Start</div>' +
    '</body>'+
    '</html>';

    response.writeHead(200, {"Content-Type": "text/html"});
    response.write(body);
    response.end();
}

function upload(response) {
    console.log("Request handler 'upload' was called");
    response.writeHead(200, {"Content-Type": "text/plain"}); 
    response.write("Hello Upload");
    response.end();
}

exports.start = start;
exports.upload = upload;

And here's the stack trace:

C02RF8L7FVH5:HelloWorld francop$ node index.js Server has started. Request for / received. About to route a request for / Request handler 'start' was called. About to route a request for / Request handler 'start' was called. events.js:141 throw er; // Unhandled 'error' event ^

Error: write after end at ServerResponse.OutgoingMessage.write (_http_outgoing.js:430:15) at Object.start (/Users/francop/Documents/Apps/HelloWorld/requestHandlers.js:18:14) at route (/Users/francop/Documents/Apps/HelloWorld/router.js:5:21) at IncomingMessage. (/Users/francop/Documents/Apps/HelloWorld/server.js:19:6) at emitNone (events.js:67:13) at IncomingMessage.emit (events.js:166:7) at endReadableNT (_stream_readable.js:921:12) at nextTickCallbackWith2Args (node.js:442:9) at process._tickCallback (node.js:356:17)

The only answers I found anywhere were "It's async so use a callback" like so:

response.write(body, function(err) {
    response.end();
});

...Which throws the exact same error.

Any help with this would be amazing. The few sparse answers I've found online don't help at all.

Thanks!

Answer

David Antoon picture David Antoon · Sep 1, 2016

Return your body in response.end() instead of response.write()

function start (response) {
    console.log("Request handler 'start' was called.");
    var body = '<html>'+
    '<head>'+
    '<meta http-equiv="Content-Type" content="text/html; '+
    'charset=UTF-8" />'+
    '</head>'+
    '<body>'+
    '<div>Hello Start</div>' +
    '</body>'+
    '</html>';

    response.writeHead(200, {"Content-Type": "text/html"});
    response.end(body);
}

Edit 1:

The first time response.write() is called, it will send the buffered header information and the first body to the client. The second time response.write() is called, Node.js assumes you're going to be streaming data, and sends that separately. That is, the response is buffered up to the first chunk of body.

So, if you want to send only one chunk, you must use response.end(body), and if you want to send multiple chunks you must end you called with the response.end() after multiple response.write().

Example single chunk:

function (chunk){
    response.end(chunk);
}

Example multiple chunks:

function (chunkArray){
    // loop on chunks 
    for(var i=0; i < chunkArray.length; i++){
        response.write(chunkArray[i]);                
    }

    // end the response with empty params
    response.end();
}