discordjs / discord.js

A powerful JavaScript library for interacting with the Discord API
https://discord.js.org
Apache License 2.0
25.36k stars 3.97k forks source link

Unhandled 'error' event: "WebSocket was closed before the connection was established" when client was connected and network goes down #7964

Closed crycode-de closed 2 years ago

crycode-de commented 2 years ago

Which package is this bug report for?

discord.js

Issue description

Steps to reproduce:

  1. Create a very simple bot like the code sample below
  2. Let the bot connect to discord
  3. Destroy the network/internet connection of the machine running the script (e.g. unplug the (virtual) network cable)
  4. Wait until the bot tries to reconnect
  5. See the app crashes on an unhandled 'error' event
    Error: WebSocket was closed before the connection was established
    at WebSocket.close (/home/peter/tmp/node_modules/ws/lib/websocket.js:285:14)
    at WebSocketShard.destroy (/home/peter/tmp/node_modules/discord.js/src/client/websocket/WebSocketShard.js:728:27)
    at Timeout.<anonymous> (/home/peter/tmp/node_modules/discord.js/src/client/websocket/WebSocketShard.js:522:12)
    at listOnTimeout (node:internal/timers:559:17)
    at processTimers (node:internal/timers:502:7)
    Emitted 'error' event on WebSocket instance at:
    at emitErrorAndClose (/home/peter/tmp/node_modules/ws/lib/websocket.js:984:13)
    at processTicksAndRejections (node:internal/process/task_queues:83:21)

Full log: log.txt

There seams to be no way to handle the WebSocket error event.

Expected behavior would be, that discord.js keeps trying to reestablish the connection, or that there is some chance to handle the error.

Came up with this, as my internet connection was gone away and my application using discord.js crashed with the above error.

Code sample

const { Client, Intents } = require('discord.js');

const token = 'xxx';

async function main () {
  const client = new Client({
    intents: [
      Intents.FLAGS.GUILDS,
    ],
  });

  client.on('debug', console.debug);
  client.on('warn', console.warn);
  client.on('error', console.error);

  client.on('ready', () => console.log('ready'));

  await client.login(token);
}

main();

Package version

13.7.0

Node.js version

16.14.2

Operating system

Debian 10

Priority this issue should have

Medium (should be fixed soon)

Which partials do you have configured?

No Partials

Which gateway intents are you subscribing to?

Guilds

I have tested this issue on a development release

No response

IxPrumxI commented 2 years ago

i don't think you even replugged the network again as you didn't mention it, so why would this be a bug?

crycode-de commented 2 years ago

i don't think you even replugged the network again as you didn't mention it, so why would this be a bug?

That's right, but the application shouldn't crash with an unhandled 'error' event. There seams to be no way to handle the WebSocket error. Even tried to handle the shardError event, but without success.

Expected behavior would be, that discord.js keeps trying to reestablish the connection, or that there is some chance to handle the error.

Edit: I've updated the description above.

kyranet commented 2 years ago

I must ask, are you able to reproduce this with v14?

We are attaching ws.onerror along other events, I also expanded your stack within ws's code to get a better look at what's happening:

And for reference, onclose should be all that's necessary:

crycode-de commented 2 years ago

Same error with v14 (14.0.0-dev.1653091711-fdeac9d)

Example code:

const { Client, GatewayIntentBits  } = require('discord.js');

const token = 'xxx';

async function main () {
  const client = new Client({
    intents: [
      GatewayIntentBits.Guilds,
    ],
  });

  client.on('debug', console.debug);
  client.on('warn', console.warn);
  client.on('error', console.error);

  client.on('ready', () => console.log('ready'));

  await client.login(token);
}

main();
Logs ``` Provided token: ODEzMzY0MzU0OTgzNjU3NDcy.YDOOlQ.*************************** Preparing to connect to the gateway... [WS => Manager] Fetched Gateway Information URL: wss://gateway.discord.gg Recommended Shards: 1 [WS => Manager] Session Limit Information Total: 1000 Remaining: 999 [WS => Manager] Spawning shards: 0 [WS => Shard 0] [CONNECT] Gateway : wss://gateway.discord.gg/ Version : 10 Encoding : json Compression: none [WS => Shard 0] Setting a HELLO timeout for 20s. [WS => Shard 0] [CONNECTED] Took 212ms [WS => Shard 0] Clearing the HELLO timeout. [WS => Shard 0] Setting a heartbeat interval for 41250ms. [WS => Shard 0] [IDENTIFY] Shard 0/1 with intents: 1 [WS => Shard 0] [READY] Session 0dc4eeb1d880cc8b1aab826876829531. [WS => Shard 0] [ReadyHeartbeat] Sending a heartbeat. [WS => Shard 0] Shard received all its guilds. Marking as fully ready. ready [WS => Shard 0] Heartbeat acknowledged, latency of 119ms. [WS => Shard 0] [HeartbeatTimer] Sending a heartbeat. [WS => Shard 0] Heartbeat acknowledged, latency of 122ms. [WS => Shard 0] [HeartbeatTimer] Sending a heartbeat. [WS => Shard 0] [HeartbeatTimer] Didn't receive a heartbeat ack last time, assuming zombie connection. Destroying and reconnecting. Status : 0 Sequence : 2 Connection State: OPEN [WS => Shard 0] [DESTROY] Close Code : 4009 Reset : true Emit DESTROYED: true [WS => Shard 0] Clearing the heartbeat interval. [WS => Shard 0] [CLOSE] Event Code: 1006 Clean : false Reason : [WS => Shard 0] [CONNECT] Gateway : wss://gateway.discord.gg/ Version : 10 Encoding : json Compression: none [WS => Shard 0] Setting a HELLO timeout for 20s. [WS => Shard 0] [CLOSE] Event Code: 1006 Clean : false Reason : [WS => Shard 0] Clearing the HELLO timeout. [WS => Shard 0] WS State: CLOSED [WS => Shard 0] Failed to connect to the gateway, requeueing... [WS => Manager] Shard Queue Size: 1; continuing in 5 seconds... [WS => Shard 0] [CONNECT] Gateway : wss://gateway.discord.gg/ Version : 10 Encoding : json Compression: none [WS => Shard 0] Setting a HELLO timeout for 20s. [WS => Shard 0] Did not receive HELLO in time. Destroying and connecting again. [WS => Shard 0] [DESTROY] Close Code : 4009 Reset : true Emit DESTROYED: true [WS => Shard 0] Clearing the HELLO timeout. [WS => Shard 0] WS State: CONNECTING [WS => Shard 0] Shard was destroyed but no WebSocket connection was present! Reconnecting... node:events:504 throw er; // Unhandled 'error' event ^ Error: WebSocket was closed before the connection was established at WebSocket.close (/home/peter/tmp/node_modules/ws/lib/websocket.js:285:14) at WebSocketShard.destroy (/home/peter/tmp/node_modules/discord.js/src/client/websocket/WebSocketShard.js:731:27) at Timeout. (/home/peter/tmp/node_modules/discord.js/src/client/websocket/WebSocketShard.js:525:12) at listOnTimeout (node:internal/timers:559:17) at processTimers (node:internal/timers:502:7) Emitted 'error' event on WebSocket instance at: at emitErrorAndClose (/home/peter/tmp/node_modules/ws/lib/websocket.js:984:13) at processTicksAndRejections (node:internal/process/task_queues:83:21) ```
kyranet commented 2 years ago

Yeah, I see the issue now, prior to calling this.connection.close, we are calling this line:

https://github.com/discordjs/discord.js/blob/fdeac9d9fba06c532eca296ddd8479047bc732bf/packages/discord.js/src/client/websocket/WebSocketShard.js#L728

Which removes all the listeners:

https://github.com/discordjs/discord.js/blob/fdeac9d9fba06c532eca296ddd8479047bc732bf/packages/discord.js/src/client/websocket/WebSocketShard.js#L772

And because we are closing while the WS is in a connecting state, it emits an abortion error:

https://github.com/websockets/ws/blob/a690791df31cede48e7bcbce9d56411483309246/lib/websocket.js#L283-L286 -> https://github.com/websockets/ws/blob/a690791df31cede48e7bcbce9d56411483309246/lib/websocket.js#L1031-L1056

At this line within the last function, ws.onerror would be called:

https://github.com/websockets/ws/blob/a690791df31cede48e7bcbce9d56411483309246/lib/websocket.js#L1053

But because we removed the listeners earlier (and they were the only one present), we were not able to handle them.

We could perhaps move the this._cleanupConnection(); call to after the this.connection.close() call to avoid this error, can you patch the code and try if that works? It should in essence be the following change:

image

cc: @vladfrangu

imranbarbhuiya commented 2 years ago

ig it's related to https://github.com/discordjs/discord.js/pull/7581

crycode-de commented 2 years ago

We could perhaps move the this._cleanupConnection(); call to after the this.connection.close() call to avoid this error, can you patch the code and try if that works? It should in essence be the following change:

I've tried this, but the error stays the same.

grafik

kyranet commented 2 years ago

Can't confirm without debugging (please use one!), but I suppose it's because this line is called:

https://github.com/websockets/ws/blob/a690791df31cede48e7bcbce9d56411483309246/lib/websocket.js#L1050

That does... complicate some things. We could process.nextTick(() => this._cleanupConnection()); (still after this.connection.close, so it's queued after), but I'm unsure if that'll break something.

legendhimself commented 2 years ago

ig it's related to #7581

umm, I don't think so. Why? Because during close code 4009 it won't execute the else block which has _cleanupConnection method, Since the above if statement is true. I am saying this as I debugged this issue for almost over 2 months now. I think cleaning up the during onClose event is better[which already we are] and should be removed from destroy method. Anyways my pr auto emits the onClose if the close frame(which is responsible for onClose event) wasn't received due to any issue so cleanup will happen.

crycode-de commented 2 years ago

Changing to process.nextTick(() => this._cleanupConnection()); leads to an other error, since this.connection is null on the next tick:

Logs ``` [WS => Shard 0] [HeartbeatTimer] Didn't receive a heartbeat ack last time, assuming zombie connection. Destroying and reconnecting. Status : READY Sequence : 1 Connection State: OPEN [WS => Shard 0] [DESTROY] Close Code : 4009 Reset : true Emit DESTROYED: true [WS => Shard 0] Clearing the heartbeat interval. [WS => Shard 0] [CLOSE] Event Code: 1006 Clean : false Reason : [WS => Shard 0] [CONNECT] Gateway : wss://gateway.discord.gg/ Version : 9 Encoding : json Compression: none [WS => Shard 0] Setting a HELLO timeout for 20s. [WS => Shard 0] [CLOSE] Event Code: 1006 Clean : false Reason : [WS => Shard 0] Clearing the HELLO timeout. [WS => Shard 0] WS State: CLOSED /home/peter/tmp/node_modules/discord.js/src/client/websocket/WebSocketShard.js:769 this.connection.onopen = this.connection.onclose = this.connection.onerror = this.connection.onmessage = null; ^ TypeError: Cannot set properties of null (setting 'onmessage') at WebSocketShard._cleanupConnection (/home/peter/tmp/node_modules/discord.js/src/client/websocket/WebSocketShard.js:769:108) at /home/peter/tmp/node_modules/discord.js/src/client/websocket/WebSocketShard.js:731:37 at processTicksAndRejections (node:internal/process/task_queues:78:11) ```

If I remove the call of this._cleanupConnection() it seams to work and tries reconnect every 5 seconds, but don't know if this might lead to other issues:

Logs ``` [WS => Shard 0] [HeartbeatTimer] Didn't receive a heartbeat ack last time, assuming zombie connection. Destroying and reconnecting. Status : READY Sequence : 1 Connection State: OPEN [WS => Shard 0] [DESTROY] Close Code : 4009 Reset : true Emit DESTROYED: true [WS => Shard 0] Clearing the heartbeat interval. [WS => Shard 0] [CLOSE] Event Code: 1006 Clean : false Reason : [WS => Shard 0] [CONNECT] Gateway : wss://gateway.discord.gg/ Version : 9 Encoding : json Compression: none [WS => Shard 0] Setting a HELLO timeout for 20s. [WS => Shard 0] [CLOSE] Event Code: 1006 Clean : false Reason : [WS => Shard 0] Clearing the HELLO timeout. [WS => Shard 0] WS State: CLOSED [WS => Shard 0] Failed to connect to the gateway, requeueing... [WS => Manager] Shard Queue Size: 1; continuing in 5 seconds... [WS => Shard 0] [CONNECT] Gateway : wss://gateway.discord.gg/ Version : 9 Encoding : json Compression: none [WS => Shard 0] Setting a HELLO timeout for 20s. [WS => Shard 0] [CLOSE] Event Code: 1006 Clean : false Reason : [WS => Shard 0] Clearing the HELLO timeout. [WS => Shard 0] WS State: CLOSED [WS => Shard 0] Failed to connect to the gateway, requeueing... [WS => Manager] Shard Queue Size: 1; continuing in 5 seconds... [WS => Shard 0] [CONNECT] Gateway : wss://gateway.discord.gg/ Version : 9 Encoding : json Compression: none [WS => Shard 0] Setting a HELLO timeout for 20s. [WS => Shard 0] [CLOSE] Event Code: 1006 Clean : false Reason : [WS => Shard 0] Clearing the HELLO timeout. [WS => Shard 0] WS State: CLOSED [WS => Shard 0] Failed to connect to the gateway, requeueing... [WS => Manager] Shard Queue Size: 1; continuing in 5 seconds... [WS => Shard 0] [CONNECT] Gateway : wss://gateway.discord.gg/ Version : 9 Encoding : json Compression: none [WS => Shard 0] Setting a HELLO timeout for 20s. [WS => Shard 0] [CLOSE] Event Code: 1006 Clean : false Reason : [WS => Shard 0] Clearing the HELLO timeout. [WS => Shard 0] WS State: CLOSED [WS => Shard 0] Failed to connect to the gateway, requeueing... [WS => Manager] Shard Queue Size: 1; continuing in 5 seconds... [WS => Shard 0] [CONNECT] ```
legendhimself commented 2 years ago

@crycode-de I tried to recreate this issue but I am failing to recreate the exact same thing. With djs v13.7.0 it reconnects perfectly fine after i plug/unplug. Also can you please try to replicate this issue using my pr https://github.com/discordjs/discord.js/pull/7581 ?

You can download it from here npm => npm install github:legendhimslef/discord.js#v13 yarn => yarn add discord.js@github:legendhimslef/discord.js#v13

crycode-de commented 2 years ago

@legendhimslef Using your PR #7581 seams to work fine. The error then gets emitted as shardError and a reconnect is queued. šŸ‘

fredkilbourn commented 2 years ago

I just had this same error today using discord.js 13.8.0:

Uncaught Exception:
Error: WebSocket was closed before the connection was established
    at WebSocket.close (/home/vbot/srv/vbot/node_modules/ws/lib/websocket.js:285:14)
    at WebSocketShard.destroy (/home/vbot/srv/vbot/node_modules/discord.js/src/client/websocket/WebSocketShard.js:806:27)
    at WebSocketShard.onClose (/home/vbot/srv/vbot/node_modules/discord.js/src/client/websocket/WebSocketShard.js:370:12)
    at WebSocket.onClose (/home/vbot/srv/vbot/node_modules/ws/lib/event-target.js:210:18)
    at WebSocket.emit (node:events:526:28)
    at WebSocket.emitClose (/home/vbot/srv/vbot/node_modules/ws/lib/websocket.js:258:10)
    at Receiver.receiverOnFinish (/home/vbot/srv/vbot/node_modules/ws/lib/websocket.js:1158:20)
    at Receiver.emit (node:events:526:28)
    at finish (node:internal/streams/writable:754:10)
    at processTicksAndRejections (node:internal/process/task_queues:83:21)
legendhimself commented 2 years ago

I just had this same error today using discord.js 13.8.0:

Uncaught Exception:
Error: WebSocket was closed before the connection was established
    at WebSocket.close (/home/vbot/srv/vbot/node_modules/ws/lib/websocket.js:285:14)
    at WebSocketShard.destroy (/home/vbot/srv/vbot/node_modules/discord.js/src/client/websocket/WebSocketShard.js:806:27)
    at WebSocketShard.onClose (/home/vbot/srv/vbot/node_modules/discord.js/src/client/websocket/WebSocketShard.js:370:12)
    at WebSocket.onClose (/home/vbot/srv/vbot/node_modules/ws/lib/event-target.js:210:18)
    at WebSocket.emit (node:events:526:28)
    at WebSocket.emitClose (/home/vbot/srv/vbot/node_modules/ws/lib/websocket.js:258:10)
    at Receiver.receiverOnFinish (/home/vbot/srv/vbot/node_modules/ws/lib/websocket.js:1158:20)
    at Receiver.emit (node:events:526:28)
    at finish (node:internal/streams/writable:754:10)
    at processTicksAndRejections (node:internal/process/task_queues:83:21)

This was already patched in 13.8.0 Updating that we now have the this pr to fix some stuff: https://github.com/discordjs/discord.js/pull/8082 We might have this šŸ‘† in 13.8.1

fredkilbourn commented 2 years ago

Thanks!

wdfeww commented 2 years ago

same on 13.8.1 :(

node:events:505
      throw er; // Unhandled 'error' event
      ^

Error: WebSocket was closed before the connection was established
    at WebSocket.close (/root/fs-dc/node_modules/ws/lib/websocket.js:285:14)
    at WebSocketShard.destroy (/root/fs-dc/node_modules/discord.js/src/client/websocket/WebSocketShard.js:806:27)
    at WebSocketShard.onClose (/root/fs-dc/node_modules/discord.js/src/client/websocket/WebSocketShard.js:370:12)
    at WebSocket.onClose (/root/fs-dc/node_modules/ws/lib/event-target.js:210:18)
    at WebSocket.emit (node:events:527:28)
    at WebSocket.emitClose (/root/fs-dc/node_modules/ws/lib/websocket.js:258:10)
    at Receiver.receiverOnFinish (/root/fs-dc/node_modules/ws/lib/websocket.js:1158:20)
    at Receiver.emit (node:events:527:28)
    at finish (node:internal/streams/writable:754:10)
    at processTicksAndRejections (node:internal/process/task_queues:83:21)
Emitted 'error' event on WebSocket instance at:
    at emitErrorAndClose (/root/fs-dc/node_modules/ws/lib/websocket.js:993:13)
    at processTicksAndRejections (node:internal/process/task_queues:83:21)
legendhimself commented 2 years ago

same on 13.8.1 :(

node:events:505
      throw er; // Unhandled 'error' event
      ^

Error: WebSocket was closed before the connection was established
    at WebSocket.close (/root/fs-dc/node_modules/ws/lib/websocket.js:285:14)
    at WebSocketShard.destroy (/root/fs-dc/node_modules/discord.js/src/client/websocket/WebSocketShard.js:806:27)
    at WebSocketShard.onClose (/root/fs-dc/node_modules/discord.js/src/client/websocket/WebSocketShard.js:370:12)
    at WebSocket.onClose (/root/fs-dc/node_modules/ws/lib/event-target.js:210:18)
    at WebSocket.emit (node:events:527:28)
    at WebSocket.emitClose (/root/fs-dc/node_modules/ws/lib/websocket.js:258:10)
    at Receiver.receiverOnFinish (/root/fs-dc/node_modules/ws/lib/websocket.js:1158:20)
    at Receiver.emit (node:events:527:28)
    at finish (node:internal/streams/writable:754:10)
    at processTicksAndRejections (node:internal/process/task_queues:83:21)
Emitted 'error' event on WebSocket instance at:
    at emitErrorAndClose (/root/fs-dc/node_modules/ws/lib/websocket.js:993:13)
    at processTicksAndRejections (node:internal/process/task_queues:83:21)

steps to recreate?

imranbarbhuiya commented 2 years ago

https://github.com/discordjs/discord.js/pull/8164 will fix it

legendhimself commented 2 years ago

https://github.com/discordjs/discord.js/pull/8164 will fix it

wait whats the point of silencing errors. We already have issues with socket hangups.

imranbarbhuiya commented 2 years ago

u can read https://github.com/discordjs/discord.js/pull/8150 description

ASHISHKUMAR2411 commented 2 years ago

at Timeout.onConnectTimeout [as _onTimeout] (D:\path\smart_contracts\node_modules\undici\lib\core\connect.js:108:24)
at listOnTimeout (node:internal/timers:559:11)
at processTimers (node:internal/timers:500:7) {
code: 'UND_ERR_CONNECT_TIMEOUT'
}```
I am trying to deploy my smart contract on alchemy using rinkeby testnet `npx hardhat verify --network rinkeby address`
pedroricardo commented 2 years ago

"discord.js": "13.9.0",

node:events:504
+      throw er; // Unhandled 'error' event
      ^
BError: WebSocket was closed before the connection was established
L    at WebSocket.close (/home/node/node_modules/ws/lib/websocket.js:285:14)
q    at WebSocketShard.destroy (/home/node/node_modules/discord.js/src/client/websocket/WebSocketShard.js:719:27)
o    at WebSocketShard._send (/home/node/node_modules/discord.js/src/client/websocket/WebSocketShard.js:659:12)
v    at WebSocketShard.processQueue (/home/node/node_modules/discord.js/src/client/websocket/WebSocketShard.js:685:12)
n    at WebSocketShard.send (/home/node/node_modules/discord.js/src/client/websocket/WebSocketShard.js:647:10)
w    at WebSocketManager.broadcast (/home/node/node_modules/discord.js/src/client/websocket/WebSocketManager.js:312:53)
f    at ClientPresence.set (/home/node/node_modules/discord.js/src/structures/ClientPresence.js:25:22)
g    at ClientUser.setPresence (/home/node/node_modules/discord.js/src/structures/ClientUser.js:120:33)
g    at ClientUser.setActivity (/home/node/node_modules/discord.js/src/structures/ClientUser.js:167:17)
H    at Timeout._onTimeout (/home/node/src/events/client/ready.js:31:44)
iCrawl commented 2 years ago

@pedroricardo Do you have an error handler attached to your client?

client.on('error', console.error);
pedroricardo commented 2 years ago

@iCrawl I do not have

SpaceEEC commented 2 years ago

The stacktrace suggests that you are running 13.4.0 (or even older): Please make sure you are actually running 13.9.2 where this issue should no longer occur.

iCrawl commented 2 years ago

@pedroricardo Additionally to the thing above, please also have an error handler like I showed.