undici icon indicating copy to clipboard operation
undici copied to clipboard

SocketError: other side closed (with repro example)

Open nordluf opened this issue 1 year ago • 3 comments

Bug Description

SocketError: other side closed

Reproducible By

https://github.com/nordluf/fetch-socket-closed-example Here is the complete example

Expected Behavior

I expect fetch to stream any amount of data after any number of requests

Logs & Screenshots

TypeError: fetch failed at node:internal/deps/undici/undici:12502:13 at async streamFetch (file:///srv/www/example.mjs:52:18) at async file:///srv/www/example.mjs:16:1 { [cause]: SocketError: other side closed at Socket. (/srv/www/node_modules/undici/lib/dispatcher/client-h1.js:681:24) at Socket.emit (node:events:531:35) at endReadableNT (node:internal/streams/readable:1696:12) at process.processTicksAndRejections (node:internal/process/task_queues:82:21) { code: 'UND_ERR_SOCKET', socket: { localAddress: '127.0.0.1', localPort: 50476, remoteAddress: '127.0.0.1', remotePort: 3000, remoteFamily: 'IPv4', timeout: undefined, bytesWritten: 360, bytesRead: 10270430 } } }

Environment

Node.js v20.13.1, Linux

Additional context

There are plenty of other users affected by this issue

nordluf avatar May 29 '24 09:05 nordluf

I am also getting this error on Next.js 14 fetching data within a server component within a Suspense boundary. Error does not occur with regular (non-streamed) fetches (i.e. fetches not made within Suspense boundaries).

charlesfrisbee avatar Jun 15 '24 08:06 charlesfrisbee

Hi! I have the exact same issue right now on node 20.15.

It seems to fail randomly. When it fails, the failed request doesn't even reach its destination. In my case I control both the client and the API, and the failed requests doesn't appear anywhere in the API logs.

I've seen numerous issues mentioning this, all closed without a solution. We're here to provides more info or context if needed.

p-cauty avatar Jun 24 '24 15:06 p-cauty

有代理的情况下也会有,100%必现!

baiyuze avatar Jun 25 '24 02:06 baiyuze

Version 20.15.1 still buggy

nordluf avatar Jul 10 '24 17:07 nordluf

Ran into this issue as well on v20 and also on v22 in a NextJS build where during buildtime a lot of fetches are made. Downgrading to v18 seemed to work, interestingly enough.

pepijn-vanvlaanderen avatar Aug 06 '24 08:08 pepijn-vanvlaanderen

I still see the issue using node 18. You can change image: node:18-slim in docker-compose.yaml to see the failing output

nordluf avatar Aug 06 '24 08:08 nordluf

The issue seems to root to the actual server. I've tried to reproduce your example, and although i reproduce it successfully, the issue goes to the server closing the connection.

If the server closes the connection, this will inevitably lead to undici not being able to send anymore request.

Currently, agents does not reconnect by themselves, so a new connection needs to be attempted.

metcoder95 avatar Aug 16 '24 08:08 metcoder95

Of course, if the server closes the connection the undici(fetch) is not guilty This is the smallest (and easiest to reuse by others) possible example of reproducing the issue. I have seen the error from undici (fetch) even with other HTTP servers like nginx. It always happens after fetching a lot of data.

If you need any other type of support or any other data from my side to be shared - feel free to ping me

nordluf avatar Aug 16 '24 09:08 nordluf

Can you provide an Minimum Reproducible Example without Express to reproduce this?

I'd like to have something closer to what you are seeing to debug it better. The issue seems to root to undici itself, not to fetch; but want to be sure what can be causing the problem

metcoder95 avatar Aug 19 '24 06:08 metcoder95

If you call res.write without a callback then you are basically flooding the socket without any flow control. Basically the server was crashing.

people who claim to run in a similar issue have probably a different issue where the SocketError is thrown.

'use strict'

const { fetch } = require('./undici-fetch.js');
const { Readable } = require('node:stream');
const { once } = require('node:events');
const { createServer } = require('node:http');
const { promisify } = require('node:util');

async function init() {
  const server = createServer(async (req, res) => {
    const url = new URL(req.url, `http://example.org`);

    res.writeHead(200, { 'Content-Type': 'text/plain' });
    const size = +url.searchParams.get('size');
    const count = +url.searchParams.get('count')

    const strObj = Buffer.allocUnsafe(+size).toString('ascii')

    const resWrite = promisify(res.write.bind(res));
    await resWrite('[');
    for (let i = 0; i < count; i++) {
      await resWrite(strObj + ',');
    }
    await resWrite(strObj);
    await resWrite(']');
    res.end();
  })

  server.listen(3000);
  await once(server, 'listening');
  return server;
}

async function streamFetch(size, count) {
  const result = await fetch(`http://127.0.0.1:3000?size=${size}&count=${count}`, { isStream: true });
  if (!result.ok) {
    throw new Error(`HTTP error! Status: ${result.status}`);
  }
  if (!result.body) {
    throw new Error('No result body!');
  }
  const stream = Readable.fromWeb(result.body, { encoding: 'utf8' });

  console.log('Request sent.');
  let kb = 0, lastkb = 0;
  return new Promise((resolve, reject) => {
    stream.on('error', reject);
    stream.on('end', resolve);
    stream.on('data', (chunk) => {
      kb += chunk.length;
      if (kb - lastkb > 10 * 1024 * 1024) {
        console.log('Another 10M received');
        lastkb = kb;
        // console.log(chunk)
      }
    });
  });
}

async function run() {
  const server = await init();
  
  await streamFetch(1024, 100);
  await streamFetch(10240, 1000);
  await streamFetch(10240, 10000);
  await streamFetch(10240, 100000);
  await streamFetch(10240, 1000000);
  await streamFetch(10240, 10000000);
  
  server.close();
}

run()

Uzlopak avatar Aug 23 '24 23:08 Uzlopak

always happening occasionally and randomly with node.js fetch, with any server... never happens with deno.

ledlamp avatar Nov 04 '24 20:11 ledlamp

I ran into this issue because my local setup was using HTTPS, but the requests were being made over HTTP. I have a proxy in my Next.js app that forwards requests to an external API, while attaching the auth cookies. For some reason, the requests weren’t being sent over HTTPS, which caused the problem.

Once I made sure the requests were being forwarded to the proxy with HTTPS, everything started working fine.

danieljcksn avatar Dec 12 '24 14:12 danieljcksn

Just stumbled upon this... Any known workarounds?

eladchen-lusha avatar Apr 01 '25 08:04 eladchen-lusha

workaround is to retry fetch on failure. you can use an http library or a simple fetch wrapper like fetch-retry on npm (or write your own in a few lines)

ledlamp avatar Apr 01 '25 09:04 ledlamp

same here any news on this?

takefy-dev avatar May 06 '25 18:05 takefy-dev