Geonode logo
Network errors

How to Fix the Socket Hang Up Error in Node.js

Work out whether a reused connection, your own timer or the server closed it, with Node.js code for each fix.

Updated

TL;DR

Error: socket hang up means Node.js saw the connection close before any response arrived: the server or a proxy ended it, or your code destroyed the request. When a safe-to-repeat request fails this way on a reused keep-alive connection, resending it on a new connection fixes it.

What socket hang up looks like in each client

Each row is a connection that closed before any reply; the TLS row closed during the handshake.

WhereWhat you see
Node.js http and https (code: 'ECONNRESET' printed below)Error: socket hang up
Node.js fetch() and its cause (code UND_ERR_SOCKET)TypeError: fetch failed ... [cause]: SocketError: other side closed
axios in Node.js, as console.log prints itAxiosError: socket hang up
Node.js https, closed during the TLS handshakeError: Client network socket disconnected before secure TLS connection was established
npm 11npm error network request to https://registry.npmjs.org/left-pad failed, reason: socket hang up
Python requestsrequests.exceptions.ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
curlcurl: (52) Empty reply from server

Why this happens

The other end, or your own code, closed the connection before any reply came back.

Node.js raises it when the socket ends while the request still has no response. A common trigger is a keep-alive connection that the server closes just as your client sends the next request on it.

Diagnose your socket hang up first

Each check ties the early close to one cause, so you change only that part.

  • Check the URL your code calls; if it pairs http:// with port 443 or another HTTPS port, a TLS server got plain HTTP and closed.

  • Note when it fails; if only the first request after an idle pause fails, the server closed that idle connection as you reused it.

  • Search your code for req.destroy() or abort() in a timeout handler; if it fires before the reply, your own code hung up.

  • Time the failing request; if it always ends after the same number of seconds, a fixed timer in the server or a proxy closed it.

  • Behind a proxy, rerun with curl -x and an https:// URL; Proxy CONNECT aborted means the proxy closed, Empty reply from server points past it.

Solutions ranked by effectiveness

Start with the retry, the usual fix; the next two cover your own timeouts and the servers you run.

  1. Most common fix

    Retry requests that hung up on reused connections

    Applies when the hang up hit a connection kept open from an earlier request. Node.js documents checking req.reusedSocket; resend a GET once, and treat a hang up on a fresh connection as a different cause.

    const https = require("node:https");
    const agent = new https.Agent({ keepAlive: true });
    function get(url, retried = false) {
      const req = https.get(url, { agent }, (res) => res.resume().on("end", () => console.log(res.statusCode)));
      req.on("error", (err) => {
        if (req.reusedSocket && err.message === "socket hang up" && !retried) return get(url, true);
        console.log("failed:", err.message);
      });
    }
    get("https://example.com/");
    setTimeout(() => get("https://example.com/"), 3000);
  2. Check next

    Stop your own timeout from ending requests

    Applies when the hang up comes from your own timer. Called without an error, destroy() reports socket hang up, so set the limit above the endpoint's real time and pass an Error naming it.

    const https = require("node:https");
    
    const started = Date.now();
    const req = https.get("https://example.com/report", { timeout: 60000 }, (res) => {
      res.resume().on("end", () => console.log(res.statusCode, "after", Date.now() - started, "ms"));
    });
    req.on("timeout", () => req.destroy(new Error("no data for 60 s, gave up")));
    req.on("error", (err) => console.log("failed after", Date.now() - started, "ms:", err.message));
  3. Site owners

    Reply with an error instead of crashing

    Applies when hang ups match your Node.js server restarting. An async handler that throws takes down a plain http server and every request in flight; catch the error and answer 500.

    const http = require("node:http");
    
    async function handle(req, res) {
      throw new Error("database down");
    }
    http.createServer((req, res) => {
      handle(req, res).catch((err) => {
        console.error(err);
        if (res.headersSent) return res.destroy(err);
        res.writeHead(500).end("Internal Server Error\n");
      });
    }).listen(8080);

Stop the socket hang up from coming back

Habits for client code and the servers you run, so one early close stays a logged, recoverable error.

  1. Attach an error listener to every request; without one, a single hang up stops your whole Node.js process.

  2. Move Express 4 apps to Express 5, which passes errors from async handlers to your error middleware.

  3. On Gunicorn, keep --timeout above your slowest request; by default it kills workers silent for 30 seconds.

  4. Keep each base URL's scheme and port in one setting, so http:// never meets a port that expects HTTPS.

Parallel requests to start withGeonode's docs suggest thread counts by job size; start low and scale up gradually.
Try residential proxies

Related errors

Learn more

FAQ

Your Node.js client sent a request, and the connection closed before any response arrived, so there is no status code to read. Chrome reports the same kind of close as ERR_EMPTY_RESPONSE.

Node.js gives it the code ECONNRESET, but the message tells cases apart: read ECONNRESET means a reset packet ended the connection, Error: aborted means a response was cut off midway, and socket hang up means none had arrived.

It is the same early close seen through fetch(): undici, the client inside it, raises SocketError with code UND_ERR_SOCKET, and fetch() wraps it as TypeError: fetch failed, so log err.cause to see it.

The connection closed during the TLS handshake, before Node.js could send the HTTPS request. Through a proxy, the tunnel shut after CONNECT succeeded; without one, the server ended the handshake.

npm's request to the registry, or to the proxy npm uses, closed before any reply. npm takes its proxy from the proxy and https-proxy settings or from HTTPS_PROXY and HTTP_PROXY, so check those values first.

axios passes Node's error on as AxiosError with the same message and code ECONNRESET. Its own timeout option fails with timeout of 5000ms exceeded and, by default, code ECONNABORTED, so a hang up came before that timer.

Your proxy traffic, hour by hour

Geonode's Statistics section shows hourly usage trends, so you can spot your peak hours.