[Fixed] NODE error write EPIPE

Guide to fixing NODE error write EPIPE

Mar 7, 2023 | Read time 12 minutes | Updated on Feb 4, 2024

🔔 Table of contents

Introduction

Recently I was working on a Node Express API. The API connects to a external link and downloads images. It was running fine for a couple of times and just found myself with the following error:

Error: write EPIPE

A more detailed error log looks like the following:

events.js:154
    throw er; // Unhandled 'error' event
    ^

Error: write EPIPE
    at exports._errnoException (util.js:856:11)
    at WriteWrap.afterWrite (net.js:767:14)

Typically this occurs in a Node.js environment when you try to write to a stream (like a process.stdout or a socket) that has been closed or has ended on the other side.

What does write EPIPE errors mean?

When you see a write EPIPE error in your node application, this just means that your program is trying to write to a pipe or socket where there is no process to read the data.

This is common with net and http code where the other end that we are trying to write to closed the connection.

The “EPIPE” in the error message stands for “Error: Pipe” and is a POSIX error code that indicates a broken pipe or socket connection.

To fix errors with EPIPE we can do the following:

  1. Update our code to catch and handle when EPIPE error comes up
  2. Update stream buffer size to match server and client
  3. Check for internet connectivity
  4. Check your code so that you are closing the connection correctly
  5. Upgrade to the latest version of node

1. Update our code to catch and handle when EPIPE error comes up

Since the error of EPIPE can be caused by a variety of reasons, it is best for you to update your code to handle it appropriately. As an example, we can attach it to the “error” event like so:

process.stdout.on('error', function( err ) {
    if (err.code == "EPIPE") {
        process.exit(0);
    }
});

In the above code, we check the error code and if it is EPIPE then exit the process. An alternative is to “retry” your connections.

Update: Check if the stream is writable

One other thing to check is to verify that the stream is Writable.

Before writing to a stream, check if it’s still writable. This can help avoid writing to a closed stream.

if (stream.writable) {
  stream.write(data);
}

2. Update stream buffer size to match server and client

Now if you are doing a upload application - for example your Node backend will have a route (such as /upload) and the client will call this route to upload files.

This could lead to the EPIPE error for large files.

Consider the following Node Express route to upload file.

const express = require('express');         // Express Web Server
const busboy = require('connect-busboy');   // Middleware to handle the file upload https://github.com/mscdex/connect-busboy
const path = require('path');               // Used for manipulation with path
const fs = require('fs-extra');             // Classic fs


const app = express(); // Initialize the express web server
app.use(busboy({
    highWaterMark: 2 * 1024 * 1024, // Set 2MiB buffer
})); // Insert the busboy middle-ware


/**
 * Create route /upload which handles the post request
 */
app.route('/upload').post((req, res, next) => {

    req.pipe(req.busboy); // Pipe it trough busboy

    req.busboy.on('file', (fieldname, file, filename) => {
        console.log(`Upload of '${filename}' started`);

        // Create a write stream of the new file
        const fstream = fs.createWriteStream(path.join(uploadPath, filename));
        // Pipe it trough
        file.pipe(fstream);

        // On finish of the upload
        fstream.on('close', () => {
            console.log(`Upload of '${filename}' finished`);
            res.redirect('back');
        });
    });
});

The above code provides a /upload route and using the busboy as the middleware to handle file uploads.

Now our client code looks something like this:

// client.js
const fs = require("fs");
const FormData = require("form-data");
const fetch = require("node-fetch");

var formdata = new FormData();
formdata.append(
  "file",
  fs.createReadStream("/project/verylargevideo.mp4")
);

var requestOptions = {
  method: "POST",
  body: formdata,
  redirect: "follow"
};

fetch("http://localhost:3200/upload", requestOptions)
  .then(response => response.text())
  .then(result => console.log(result))
  .catch(error => console.log("error", error));

This will work fine in most cases, but with very large files the EPIPE error can come up. This is due to the line:

fs.createReadStream("/project/verylargevideo.mp4")

The buffer size that we have set on the server (2MB) does not match the client buffer size. Now the problem is that the client buffer size and server buffer size that we have set does not match.

So when the server gets overwhelmed by the data that the client is sending through, you will get the EPIPE error.

To fix this, we just need to update our client to match the server buffer of 2MB with the highWaterMark option:

fs.createReadStream("/project/verylargevideo.mp4", { highWaterMark: 2 * 1024 * 1024 })

3. Check for internet connectivity

Additionally we have to make sure to have a stable internet connection and try again. For example if you are on a public WiFi this could be patchy and terminate your connection.

There are online tools you can use like SpeedCheck.org (https://www.speedcheck.org/) or even try to ping https://registry.npmjs.org and see the packet response times!

If your internet is good, then move to the next step.

4. Check your code so that you are closing the connection correctly

In the case of the NodeJS Lambda function, the error might be caused when the NodeJS event loop didn’t clean-up closed TCP connections from the HTTP connection pool and then the NodeJS runtime attempted to use the closed TCP connection.

Usually AWS Lambda functions run in an isolated container and each time it is invoked, the Lambda function executes in a new container.

In cases where the delay between two requests is very small, the container used in the last invoke might be reused.

To avoid these errors the following is suggested:

  • Check the function code and ensure that the processes (dependent on connection/stream) are finished before lambda completes execution.
  • Apply retry pattern which will create new connection/stream for new request.

5. Upgrade to the latest version of node

Another thing to check is your Node versions. Older versions of Node have been reported with bugs to throw the EPIPE errors randomly.

Open up the terminal and check your node version like so:

node --version

One good tool to use to manage Node versions is NVM!

To work with NVM and manage your Node versions we can do the following:

  1. For windows, we can go to the binary and install NVM located here:

https://github.com/coreybutler/nvm-windows/tags

For linux distros we can do the following:

sudo apt install curl 
curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
source ~/.profile
nvm install node 
  1. Uninstall all versions of node with the following command:

nvm uninstall <node version>

  1. Reinstall needed node version:

nvm install <node version>

To get the latest node version we can do nvm install latest

  1. Use node version just installed:

nvm use <node version>

In our case, we can use the command nvm use latest

Timeout error: write EPIPE

One fix to EPIPE problems is to simply increase the timeout of your Node API.

We can increase the timeout by updating the socket.setTimeout value of the request object

app.post('/', function (req, res, next) { {
    // 10 minute timeout just for POST to /
    req.socket.setTimeout(10 * 60 * 1000);
    
    // ...

});

In the above code, we just updated the timeout to 10 minutes

Write EPIPE error with webpack and memory leaks

The EPIPE error can also be thrown when you have memory leak problems with your Node app.

Now this could be a bit more complicated to diagnose since we have to run it a few times and do a bit of diagnostics.

One instance where I saw memory leak errors is using Webpack with the Jest testing framework.

events.js:174
      throw er; // Unhandled 'error' event
      ^

Error: write EPIPE
    at ChildProcess.target._send (internal/child_process.js:762:20)
    at ChildProcess.target.send (internal/child_process.js:634:19)
    at ChildProcessWorker.send (C:\src\mytest\node_modules\jest-worker\build\workers\ChildProcessWorker.js:291:17)
    at WorkerPool.send (C:\src\mytest\node_modules\jest-worker\build\WorkerPool.js:32:34)
    at Farm._process (C:\src\mytest\node_modules\jest-worker\build\Farm.js:129:10)
    at Farm._enqueue (C:\src\mytest\node_modules\jest-worker\build\Farm.js:152:10)
    at Farm._push (C:\src\mytest\node_modules\jest-worker\build\Farm.js:159:12)
    at Promise (C:\src\mytest\node_modules\jest-worker\build\Farm.js:90:14)
    at new Promise (<anonymous>)
    at Farm.doWork (C:\src\mytest\node_modules\jest-worker\build\Farm.js:56:12)
Emitted 'error' event at:
    at process.nextTick (internal/child_process.js:766:39)
    at process._tickCallback (internal/process/next_tick.js:61:11)

Each Jest test run is isolated from each other and it resets the require cache.

However Jest is known to have limitations on resetting the require cache - eg does not reset it properly for native modules like fs or https.

If in your Jest test suite, you have a custom module that monkey-patches a native module, then that module will stick around and between each test chewing up memory.

To fix this type of issue, we can do the following:

  • Check that when your code throws an error and the error is caught and handled. This mainly happens with calls such as Throw new Error() or promise.reject() or promise failures from incorrectly stubbed unit tests. Handle these errors in a try catch block instead of leaving them uncaught.
  • Reducing workers eg jest --maxWorkers 2

Update:

Summary

The EPIPE error usually happens when there is a mismatch with the read stream and write stream. This just means that your program is trying to write to a pipe or socket where there is no process to read the data.

To get rid and troubleshoot this error we can try the following checklist:

  1. Make sure that our code have try catch blocks and handle when EPIPE error comes up. Usually a retry since EPIPE can be intermittent!
  2. Verify that our stream buffer size to match server and client - eg the client is sending too much data to the server and resulting a pipe error
  3. Check for internet connectivity
  4. Check your code so that you are closing the connection correctly
  5. Upgrade to the latest version of node
  6. Increase the timeout settings

👋 About the Author

G'day! I am Huy a software engineer based in Australia. I have been creating design-centered software for the last 10 years both professionally and as a passion.

My aim to share what I have learnt with you! (and to help me remember 😅)

Follow along on Twitter , GitHub and YouTube