Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SocketClosedUnexpectedlyError - Unhandled Error #2624

Open
elvince opened this issue Oct 4, 2023 · 22 comments
Open

SocketClosedUnexpectedlyError - Unhandled Error #2624

elvince opened this issue Oct 4, 2023 · 22 comments
Labels

Comments

@elvince
Copy link

elvince commented Oct 4, 2023

Description

Hi,

We are experiencing issue with the client. Many times a day we have this unhandled error that is thrown away from the redis client.

We have properly set the "Error" handler but it is not catched in it. As a consequence our process is crashing multiple times per day.
It is severly impacting our customers.

We already dig into the issue list where this error was mentionned, we tried the PingInterval / SetInterval stuff as we are on Azure but it doesn't fix the issue.

I suspect this is another edge case than the load balancer that drop the connection.
If you can see how to catch properly this error so the reconnect system can reopen smoothlty the connection it will save our day.

Thanks,

Node.js Version

Node.js v18.17.1

Redis Server Version

Node.js v18.17.1

Node Redis Version

No response

Platform

Linux

Logs

2023-10-04T09:34:16.491974249Z node:events:492
2023-10-04T09:34:16.492029849Z       throw er; // Unhandled 'error' event
2023-10-04T09:34:16.492037249Z       ^
2023-10-04T09:34:16.492041649Z 
2023-10-04T09:34:16.492045549Z SocketClosedUnexpectedlyError: Socket closed unexpectedly
2023-10-04T09:34:16.492049849Z     at Socket.<anonymous> (/home/site/wwwroot/node_modules/@redis/client/dist/lib/client/socket.js:194:118)
2023-10-04T09:34:16.492054649Z     at Object.onceWrapper (node:events:629:26)
2023-10-04T09:34:16.492058649Z     at Socket.emit (node:events:514:28)
2023-10-04T09:34:16.492062649Z     at TCP.<anonymous> (node:net:323:12)
2023-10-04T09:34:16.492066948Z Emitted 'error' event on Commander instance at:
2023-10-04T09:34:16.492071048Z     at RedisSocket.<anonymous> (/home/site/wwwroot/node_modules/@redis/client/dist/lib/client/index.js:396:14)
2023-10-04T09:34:16.492075148Z     at RedisSocket.emit (node:events:514:28)
2023-10-04T09:34:16.492082148Z     at RedisSocket._RedisSocket_onSocketError (/home/site/wwwroot/node_modules/@redis/client/dist/lib/client/socket.js:217:10)
2023-10-04T09:34:16.492086248Z     at Socket.<anonymous> (/home/site/wwwroot/node_modules/@redis/client/dist/lib/client/socket.js:194:107)
2023-10-04T09:34:16.492090248Z     at Object.onceWrapper (node:events:629:26)
2023-10-04T09:34:16.492094048Z     at Socket.emit (node:events:514:28)
2023-10-04T09:34:16.492097948Z     at TCP.<anonymous> (node:net:323:12)
@elvince elvince added the Bug label Oct 4, 2023
@leibale
Copy link
Contributor

leibale commented Oct 4, 2023

"Unhandled 'error' event" means that you are not listening to 'error's... maybe you have more than one client in the code and you forgot to listen to errors on one of them?

@TDola
Copy link

TDola commented Oct 4, 2023

I am having this problem too. I only have one client, and the on error is set. But it crashes anyways. Using 4.6.10 on node 20.7.0
It will error 10 or so times on the socket, then crash, each time calling the on error handler. Very easy to reproduce, just restart the docker container for redis and your client crashes.

this.client = createClient()
this.client.on('error', err => console.log('Redis Client Error', err))
this.client.on('end', () => {
   console.log('Redis connection ended');
})
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\app\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\app\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\app\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\app\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
undefined
node:internal/process/promises:289
            triggerUncaughtException(err, true /* fromPromise */);
            ^

SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\app\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)

After tracing it, I think the error ultimately comes from something in your code making a get or set to redis while it is down.
I am getting around the problem by watching the ready event and using my own state variable to skip over gets and sets while it is disconnected.

@elvince
Copy link
Author

elvince commented Oct 5, 2023

I confirm I have only 1 client and I set the error handler.

const _redisClient = createClient(conOptions);

   _redisClient.on('ready', () => {
     console.log('Redis is ready');
   });

   _redisClient.on('reconnecting', () => {
     console.log('client is reconnecting');
   });

   _redisClient.on('end', () => {
     console.log('Redis connection ended');
   });

   _redisClient.on('connect', () => {
     global.console.log('connected');
   });

   _redisClient.on('error', (err) => {
     console.error('Redis error: ', err);
   });

   _redisClient.connect();
   return _redisClient;

Thanks,

@elvince
Copy link
Author

elvince commented Oct 6, 2023

In my case, it is not catch inside the error handler :(

If I shutdown the redis server, the error is properly managed:
2023-10-06T13:18:21.252432723Z Redis error: Error: read ECONNRESET
2023-10-06T13:18:21.252493923Z at TCP.onStreamRead (node:internal/stream_base_commons:217:20) {
2023-10-06T13:18:21.252499722Z errno: -104,
2023-10-06T13:18:21.252502922Z code: 'ECONNRESET',
2023-10-06T13:18:21.252505922Z syscall: 'read'
2023-10-06T13:18:21.252508922Z }
2023-10-06T13:18:21.252566422Z client is reconnecting

@leibale
Any tips ?
Is there a possibility that it happens when 2 command are sent at the same time?
thanks,

@bossajie
Copy link

How to fix this?

@TDola
Copy link

TDola commented Oct 13, 2023

How to fix this?

If its like my solution, double check you are not trying to set or get while the connection is down. Add a global variable for yourself to check that updates

let alive=false
 _redisClient.on('end', () => {
     alive=false
   });

    _redisClient.on('ready', () => {
     alive=true
   });

   _redisClient.on('error', (err) => {
     alive=false
   });

and before redisClient.set()

if (alive){
  redisClient.set(etc, etc2)
}

That way you don't try to set when its dead. I made a whole wrapper class around redis so it was trivial to add a check to all of them

@ap0sentada
Copy link

damn, same erro. there's no fix i see

@leibale
Copy link
Contributor

leibale commented Oct 19, 2023

@TDola you can use disableOfllineQueue to achieve the same thing. See here.

@ap0sentada @bossajie @elvince
I only managed to reproduce this when a client emits an error and there are no listeners (which is expected behavior). If this error is thrown in other cases, please share code that reproduces it..

@TDola
Copy link

TDola commented Oct 19, 2023

@TDola you can use disableOfllineQueue to achieve the same thing. See here.

@ap0sentada @bossajie @elvince I only managed to reproduce this when a client emits an error and there are no listeners (which is expected behavior). If this error is thrown in other cases, please share code that reproduces it..

You can use this code to reproduce the problem.

package.json

{
  "name": "redis-crash-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "run": "node index.js"
  },
  "type": "module",
  "author": "",
  "license": "ISC",
  "dependencies": {
    "redis": "^4.6.10"
  }
}

npm install it and run it.

import { createClient } from 'redis'
const client = createClient()
client.on('error', async err => {
    console.log('Redis Client Error', err)
})
client.on('end', () => {
    console.log('Redis connection ended');
})
client.on('ready', () => {
    console.log('redis connected')
})
await client.connect()

const createKeys = () => {
    for (let i = 0; i < 10000; i++) {
        const value = JSON.stringify({test: i}, null, 2)
        // purposely no await to simulate a busy async system
        client.set('test-key:' + performance.now(), value)
    }
}
const removeKeys = async () => {
    const results = await client.keys('test-key:*')
    for (const key of results) {
        // no await on purpose
        client.del(key)
    }
}

const runner=()=>{
    console.log('starting runner loop')
    createKeys()
    console.log('done setting keys')
    removeKeys()
    console.log('done deleting keys, waiting 1000')
    setTimeout(()=>{runner()}, 1000)
}
process.on('SIGINT', async function() {
    console.log("Caught interrupt signal");
    const results = await client.keys('test-key:*')
    for (const key of results) {
        await client.del(key)
    }
    console.log("test keys cleaned")
    process.exit()
})
runner()

leave it to run a little bit, like 10 seconds. Then shut down your redis manually, the script should crash most of the time. Fiddle with the timeout to adjust.

starting runner loop
done setting keys
done deleting keys, waiting 5000
starting runner loop
done setting keys
done deleting keys, waiting 5000
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
redis connected
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (E:\workspace\redis-crash-test\node_modules\@redis\client\dist\lib\client\socket.js:194:118)
    at Object.onceWrapper (node:events:629:26)
    at Socket.emit (node:events:514:28)
    at TCP.<anonymous> (node:net:337:12)
starting runner loop
done setting keys
done deleting keys, waiting 5000
redis connected
Redis Client Error Error: read ECONNRESET
    at TCP.onStreamRead (node:internal/stream_base_commons:217:20) {
  errno: -4077,
  code: 'ECONNRESET',
  syscall: 'read'
}
node:internal/process/promises:289
            triggerUncaughtException(err, true /* fromPromise */);
            ^

Error: read ECONNRESET
    at TCP.onStreamRead (node:internal/stream_base_commons:217:20) {
  errno: -4077,
  code: 'ECONNRESET',
  syscall: 'read'
}

Node.js v20.7.0

The real bug might be that the ready event kept getting called even though the redis server was still down. What was it connecting to?

@leibale
Copy link
Contributor

leibale commented Oct 19, 2023

@TDola its fine not awaiting a promise, but you'll have to catch the errors, otherwise it'll crash with "unhandled promise rejection" error..

I tried reproducing it using

import { createClient } from 'redis';

const client = await createClient()
  .on('error', (err) => console.log('client error', err))
  .on('ready', () => console.log('client is ready'))
  .connect();

function createKeys() {
  for (let i = 0; i < 10000; i++) {
    client.set(`text-key${i.toString()}`, i.toString()).catch(err => {
      console.error(`Error in SET ${i}`, err);
    });
  }
}

async function removeKeys() {
  for (const key of await client.keys('test-key:*')) {
    client.del(key).catch(err => {
      console.error(`Error in DEL ${key}`, err);
    });
  }
}

function runner() {
  console.log('starting runner loop');
  createKeys();

  console.log('done setting keys');
  removeKeys();

  console.log('done deleting keys, waiting 1000');
  setTimeout(runner, 1000);
}

runner();

but... it works...

@TDola
Copy link

TDola commented Oct 19, 2023

Your key set is using a number as the ID instead of test-key: so it won't find anything in the key search. I haven't had much coffee yet so I could be wrong in reading that. But if the catch solves it, then perfect that's easily implementable.

function createKeys() {
  for (let i = 0; i < 10000; i++) {
    client.set(i.toString(), i.toString()).catch(err => {
      console.error(`Error in SET ${i}`, err);
    });
  }
}

@bossajie
Copy link

We are using createCluster(). Maybe the issue is coming from createCluster() ?

@leibale
Copy link
Contributor

leibale commented Oct 19, 2023

@TDola I've fixed the example, please LMK if you find anything..

@bossajie are you listening to errors on the cluster? i.e.:

import { createCluster } from 'redis';

const cluster = await createCluster(...)
  .on('error', err => console.error('Redis Cluster Error', err)
  .connect();

@elvince
Copy link
Author

elvince commented Oct 20, 2023

@leibale
That's great that we are moving on this issue.
It is difficult to catch issue that is not reproductible.

On TDola case, it was reproductible and you fix it by reviewing the code sample.
Is there a way to catch the error on your side when it happenned so we can have more info on where to fix stuff?
or the "unhandled promise rejection" bypass you error handling process?

If I properly understand, the issue can arise on promise not awaited / catched, correct?
I will double check this point on my side (even if I already did it :D)

thanks

Edit: I checked and all calls are awaited. I hope we can have a way to track this kind of bugs.
It seems to happen when no call are made during a long period.
I'm on azure app container and I already set the timeout parameter as per your recommendations

 const conOptions: RedisClientOptions = {
    password: xxxx
    socket: {
      host: redisOptions.host,
      port: 6666,
      connectTimeout: 2000,
    },
    pingInterval: 5 * 60 * 1000, //Ping Each 5min. https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-best-practices-connection#idle-timeout
  };

@kjxbyz
Copy link

kjxbyz commented Feb 23, 2024

The application(nest.js) is hosted on render, and this error is often reported. Redis uses the vercel kv service.

CacheModule.registerAsync({
      isGlobal: true,
      useFactory: async () => {
        const store = await redisStore({
          socket: {
            host: process.env.KV_HOST,
            port: +process.env.KV_PORT,
            tls: true,
          },
          username: process.env.KV_USERNAME,
          password: process.env.KV_PASSWORD,
          ttl: 60,
        })
        return {
          store: store as unknown as CacheStore,
        }
      },
    }),
    ThrottlerModule.forRootAsync({
      useFactory: () => ({
        throttlers: [{ limit: 10, ttl: seconds(60) }],
        storage: new ThrottlerStorageRedisService({
          host: process.env.KV_HOST,
          port: +process.env.KV_PORT,
          username: process.env.KV_USERNAME,
          password: process.env.KV_PASSWORD,
          tls: true,
          maxRetriesPerRequest: 20,
        } as unknown as RedisOptions),
      }),
    }),
node:events:495
      throw er; // Unhandled 'error' event
      ^
SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at TLSSocket.<anonymous> (/opt/render/project/src/node/node_modules/.pnpm/@[email protected]/node_modules/@redis/client/dist/lib/client/socket.js:194:118)
    at Object.onceWrapper (node:events:632:26)
    at TLSSocket.emit (node:events:529:35)
    at node:net:350:12
    at TCP.done (node:_tls_wrap:657:7)
Emitted 'error' event on Commander instance at:
    at RedisSocket.<anonymous> (/opt/render/project/src/node/node_modules/.pnpm/@[email protected]/node_modules/@redis/client/dist/lib/client/index.js:412:14)
==> Requesting node version 18
==> Using Node version 18.19.1 via /opt/render/project/src/node/.nvmrc
==> Docs on specifying a Node version: https://render.com/docs/node-version
==> Running 'pnpm start'
> homing-pigeon-workspace@ start /opt/render/project/src/node
> pnpm --filter server start
> [email protected] start /opt/render/project/src/node/server
> cross-env NODE_ENV=production node dist/main
    at RedisSocket.emit (node:events:517:28)
    at RedisSocket._RedisSocket_onSocketError (/opt/render/project/src/node/node_modules/.pnpm/@[email protected]/node_modules/@redis/client/dist/lib/client/socket.js:218:10)
    at TLSSocket.<anonymous> (/opt/render/project/src/node/node_modules/.pnpm/@[email protected]/node_modules/@redis/client/dist/lib/client/socket.js:194:107)
    at Object.onceWrapper (node:events:632:26)
    [... lines matching original stack trace ...]
    at TCP.done (node:_tls_wrap:657:7)
Node.js v18.19.1
/opt/render/project/src/node/server:
 ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL  [email protected] start: `cross-env NODE_ENV=production node dist/main`
Exit status 1
 ELIFECYCLE  Command failed with exit code 1.

EDIT

node:events:495
      throw er; // Unhandled 'error' event
      ^
SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at TLSSocket.<anonymous> (/opt/render/project/src/node_modules/.pnpm/@[email protected]/node_modules/@redis/client/dist/lib/client/socket.js:194:118)
    at Object.onceWrapper (node:events:632:26)
    at TLSSocket.emit (node:events:529:35)
    at node:net:350:12
    at TCP.done (node:_tls_wrap:657:7)
Emitted 'error' event on Commander instance at:
    at RedisSocket.<anonymous> (/opt/render/project/src/node_modules/.pnpm/@[email protected]/node_modules/@redis/client/dist/lib/client/index.js:412:14)
    at RedisSocket.emit (node:events:517:28)
    at RedisSocket._RedisSocket_onSocketError (/opt/render/project/src/node_modules/.pnpm/@[email protected]/node_modules/@redis/client/dist/lib/client/socket.js:218:10)
    at TLSSocket.<anonymous> (/opt/render/project/src/node_modules/.pnpm/@[email protected]/node_modules/@redis/client/dist/lib/client/socket.js:194:107)
    at Object.onceWrapper (node:events:632:26)
    [... lines matching original stack trace ...]
    at TCP.done (node:_tls_wrap:657:7)

@git-santosh
Copy link

I am using docker to run Redis and as client, I am using node-redis. when I am executing my code I am getting
Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly
below is my code

import { createClient } from "redis";
const client = await createClient({
  socket: {
    host: "127.0.0.1",
    port: 6379,
    legacyMode: true,
  },
})
.on('error', err => console.log('Redis Client Error', err))
await client.connect().catch(console.error);
console.log('redis connected...')

what is wrong in the code and how should we fix it ?

@greim
Copy link

greim commented May 3, 2024

I've been having the same/similar issue. In my case I'm using createCluster().

The symptoms are that after a few minutes, Node.js will predictably crash with an unhandled SocketClosedUnexpectedlyError, even though we have a listener on the 'error' event.

Interestingly, the crashes are occasionally V8 heap-allocation-related, rather than an uncaught exception. So something weird definitely seems to be happening on the socket-close condition. Could it possibly be an infinite recursive loop trying to reconnect?

At first, nothing I tried seemed to fix the issue, until I changed the client idle timeout setting from 300 to 0, effectively disabling it. Suddenly the crashing went away.

@git-santosh
Copy link

I am using docker to run Redis and as client, I am using node-redis. when I am executing my code I am getting Redis Client Error SocketClosedUnexpectedlyError: Socket closed unexpectedly below is my code

import { createClient } from "redis";
const client = await createClient({
  socket: {
    host: "127.0.0.1",
    port: 6379,
    legacyMode: true,
  },
})
.on('error', err => console.log('Redis Client Error', err))
await client.connect().catch(console.error);
console.log('redis connected...')

what is wrong in the code and how should we fix it ?

FIX :
I was using bitnami redis docker image later I used redis docker image after that I didn't see the above error.

@odavibatista
Copy link

The application(nest.js) is hosted on render, and this error is often reported. Redis uses the vercel kv service.

CacheModule.registerAsync({
      isGlobal: true,
      useFactory: async () => {
        const store = await redisStore({
          socket: {
            host: process.env.KV_HOST,
            port: +process.env.KV_PORT,
            tls: true,
          },
          username: process.env.KV_USERNAME,
          password: process.env.KV_PASSWORD,
          ttl: 60,
        })
        return {
          store: store as unknown as CacheStore,
        }
      },
    }),
    ThrottlerModule.forRootAsync({
      useFactory: () => ({
        throttlers: [{ limit: 10, ttl: seconds(60) }],
        storage: new ThrottlerStorageRedisService({
          host: process.env.KV_HOST,
          port: +process.env.KV_PORT,
          username: process.env.KV_USERNAME,
          password: process.env.KV_PASSWORD,
          tls: true,
          maxRetriesPerRequest: 20,
        } as unknown as RedisOptions),
      }),
    }),
node:events:495
      throw er; // Unhandled 'error' event
      ^
SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at TLSSocket.<anonymous> (/opt/render/project/src/node/node_modules/.pnpm/@[email protected]/node_modules/@redis/client/dist/lib/client/socket.js:194:118)
    at Object.onceWrapper (node:events:632:26)
    at TLSSocket.emit (node:events:529:35)
    at node:net:350:12
    at TCP.done (node:_tls_wrap:657:7)
Emitted 'error' event on Commander instance at:
    at RedisSocket.<anonymous> (/opt/render/project/src/node/node_modules/.pnpm/@[email protected]/node_modules/@redis/client/dist/lib/client/index.js:412:14)
==> Requesting node version 18
==> Using Node version 18.19.1 via /opt/render/project/src/node/.nvmrc
==> Docs on specifying a Node version: https://render.com/docs/node-version
==> Running 'pnpm start'
> homing-pigeon-workspace@ start /opt/render/project/src/node
> pnpm --filter server start
> [email protected] start /opt/render/project/src/node/server
> cross-env NODE_ENV=production node dist/main
    at RedisSocket.emit (node:events:517:28)
    at RedisSocket._RedisSocket_onSocketError (/opt/render/project/src/node/node_modules/.pnpm/@[email protected]/node_modules/@redis/client/dist/lib/client/socket.js:218:10)
    at TLSSocket.<anonymous> (/opt/render/project/src/node/node_modules/.pnpm/@[email protected]/node_modules/@redis/client/dist/lib/client/socket.js:194:107)
    at Object.onceWrapper (node:events:632:26)
    [... lines matching original stack trace ...]
    at TCP.done (node:_tls_wrap:657:7)
Node.js v18.19.1
/opt/render/project/src/node/server:
 ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL  [email protected] start: `cross-env NODE_ENV=production node dist/main`
Exit status 1
 ELIFECYCLE  Command failed with exit code 1.

EDIT

node:events:495
      throw er; // Unhandled 'error' event
      ^
SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at TLSSocket.<anonymous> (/opt/render/project/src/node_modules/.pnpm/@[email protected]/node_modules/@redis/client/dist/lib/client/socket.js:194:118)
    at Object.onceWrapper (node:events:632:26)
    at TLSSocket.emit (node:events:529:35)
    at node:net:350:12
    at TCP.done (node:_tls_wrap:657:7)
Emitted 'error' event on Commander instance at:
    at RedisSocket.<anonymous> (/opt/render/project/src/node_modules/.pnpm/@[email protected]/node_modules/@redis/client/dist/lib/client/index.js:412:14)
    at RedisSocket.emit (node:events:517:28)
    at RedisSocket._RedisSocket_onSocketError (/opt/render/project/src/node_modules/.pnpm/@[email protected]/node_modules/@redis/client/dist/lib/client/socket.js:218:10)
    at TLSSocket.<anonymous> (/opt/render/project/src/node_modules/.pnpm/@[email protected]/node_modules/@redis/client/dist/lib/client/socket.js:194:107)
    at Object.onceWrapper (node:events:632:26)
    [... lines matching original stack trace ...]
    at TCP.done (node:_tls_wrap:657:7)

I'm having the same issue

@samiursakib
Copy link

How to fix this?

If its like my solution, double check you are not trying to set or get while the connection is down. Add a global variable for yourself to check that updates

let alive=false
 _redisClient.on('end', () => {
     alive=false
   });

    _redisClient.on('ready', () => {
     alive=true
   });

   _redisClient.on('error', (err) => {
     alive=false
   });

and before redisClient.set()

if (alive){
  redisClient.set(etc, etc2)
}

That way you don't try to set when its dead. I made a whole wrapper class around redis so it was trivial to add a check to all of them

Hello @TDola

I implemented my redisClient class this way as you suggested. Here is the class definition.

class Client {
  alive: boolean;
  client: RedisClientType;
  constructor() {
    this.alive = false;
    this.client = createClient({
      username: process.env.REDIS_USERNAME,
      password: process.env.REDIS_PASSWORD,
      socket: {
        host: process.env.REDIS_HOST,
        port: Number(process.env.REDIS_PORT),
        tls: true,
      },
    });
    this.createConnection();
  }

  createConnection = () => {
    try {
      this.client
        .on("error", (err: any) => {
          this.alive = false;
          console.error("Redis error", err);
        })
        .on("ready", () => {
          this.alive = true;
          console.log("Client is ready");
        })
        .on("end", () => {
          this.alive = false;
          console.log("Client ended");
        })
        .connect();
    } catch (err: any) {
      console.error("Error occured during creating client", err);
    } finally {
      // await redisClient.disconnect();
    }
  };

  hSet = async (key: RedisCommandArgument, value: any) => {
    if (this.alive) {
      return await this.client.hSet(key, value);
    }
  };

  hGet = async (key: RedisCommandArgument, field: string) => {
    if (this.alive) {
      return await this.client.hGet(key, field);
    }
  };

  hGetAll = async (key: RedisCommandArgument) => {
    if (this.alive) {
      return await this.client.hGetAll(key);
    }
  };

  exists = async (key: RedisCommandArgument) => {
    if (this.alive) {
      return await this.client.exists(key);
    }
  };

  hIncrBy = async (key: RedisCommandArgument, field: string, value: number) => {
    if (this.alive) {
      return await this.client.hIncrBy(key, field, value);
    }
  };
}

export default new Client();

But this keeps the "Socket unexpectedly closed" error and also introduces a new error saying "cannot read properties of undefined (reading hSet)" which I think indicates that my client is undefined at a point of time.
Am I doing something wrong? Any suggestions will be helpful. Thanks in advance.

@TDola
Copy link

TDola commented Aug 29, 2024

How to fix this?

If its like my solution, double check you are not trying to set or get while the connection is down. Add a global variable for yourself to check that updates

let alive=false
 _redisClient.on('end', () => {
     alive=false
   });

    _redisClient.on('ready', () => {
     alive=true
   });

   _redisClient.on('error', (err) => {
     alive=false
   });

and before redisClient.set()

if (alive){
  redisClient.set(etc, etc2)
}

That way you don't try to set when its dead. I made a whole wrapper class around redis so it was trivial to add a check to all of them

Hello @TDola

I implemented my redisClient class this way as you suggested. Here is the class definition.

class Client {
  alive: boolean;
  client: RedisClientType;
  constructor() {
    this.alive = false;
    this.client = createClient({
      username: process.env.REDIS_USERNAME,
      password: process.env.REDIS_PASSWORD,
      socket: {
        host: process.env.REDIS_HOST,
        port: Number(process.env.REDIS_PORT),
        tls: true,
      },
    });
    this.createConnection();
  }

  createConnection = () => {
    try {
      this.client
        .on("error", (err: any) => {
          this.alive = false;
          console.error("Redis error", err);
        })
        .on("ready", () => {
          this.alive = true;
          console.log("Client is ready");
        })
        .on("end", () => {
          this.alive = false;
          console.log("Client ended");
        })
        .connect();
    } catch (err: any) {
      console.error("Error occured during creating client", err);
    } finally {
      // await redisClient.disconnect();
    }
  };

  hSet = async (key: RedisCommandArgument, value: any) => {
    if (this.alive) {
      return await this.client.hSet(key, value);
    }
  };

  hGet = async (key: RedisCommandArgument, field: string) => {
    if (this.alive) {
      return await this.client.hGet(key, field);
    }
  };

  hGetAll = async (key: RedisCommandArgument) => {
    if (this.alive) {
      return await this.client.hGetAll(key);
    }
  };

  exists = async (key: RedisCommandArgument) => {
    if (this.alive) {
      return await this.client.exists(key);
    }
  };

  hIncrBy = async (key: RedisCommandArgument, field: string, value: number) => {
    if (this.alive) {
      return await this.client.hIncrBy(key, field, value);
    }
  };
}

export default new Client();

But this keeps the "Socket unexpectedly closed" error and also introduces a new error saying "cannot read properties of undefined (reading hSet)" which I think indicates that my client is undefined at a point of time. Am I doing something wrong? Any suggestions will be helpful. Thanks in advance.

What you have looks pretty much the same as how I implemented it. So it might be just when you are calling it. If you are accidentally calling your sets before you have time to construct your class and wait for the connect to finish.

Also, the catches were unexpectedly important, as shown in the response to my question here
#2624 (comment)

@nullday0
Copy link

I had the same error message emitting every couple millisecond.
The issue in my case was that I was creating a client by const subClient = client.duplicate() for subscriptions but I forgot to call subClient.quit(), so number of connected clients to redis grew up quite fast causing crash or something at some point that led to all these errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests