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

JS exceptions thrown while just idling (WebSocket is already in CLOSING or CLOSED state.) #3259

Closed
1 of 2 tasks
getify opened this issue May 26, 2018 · 60 comments
Closed
1 of 2 tasks
Milestone

Comments

@getify
Copy link

getify commented May 26, 2018

You want to:

  • report a bug
  • request a feature

Current behaviour

While leaving a socket connected, but doing no other activity in my program, I see intermittently (but fairly consistently) an exception thrown in browser console, from inside of the socket.io machinery (specifically in backo2/index.js line 83. That error is:

WebSocket is already in CLOSING or CLOSED state.

socket-io-errors

Steps to reproduce (if the current behaviour is a bug)

I have two tabs open with client sockets connected to the same server (localhost) via https. Both clients are sitting idle with nothing else happening in browser or server (except for whatever keep-alive pings socket.io is doing). Both of them are joined to a single channel (via join(..) on the server). Otherwise, nothing else special.

Here's how I create the server socket instance:

var httpsServer = https.createServer(..);
var io = require("socket.io")(httpsServer);
io.on("connection",onSocketConnection);

And in the client:

socket = io();
socket.on("connect",function(){
   console.log("socket connected");
});
socket.on("disconnect",function(){
   console.log("socket disconnected");
});

Expected behaviour

I expect disconnections and reconnections from time to time, but I don't expect spurious JS exceptions thrown by the library when I'm not doing anything else over the connections.

Setup

  • OS: Mac OSX
  • browser: Chrome 66, node 10.2.1
  • socket.io version: 2.1.1

Other information (e.g. stacktraces, related issues, suggestions how to fix)

Expanded stack trace:

index.js:83 WebSocket is already in CLOSING or CLOSED state.
(anonymous) @ index.js:83
e.encodePacket @ index.js:83
(anonymous) @ index.js:83
r.write @ index.js:83
r.send @ index.js:83
r.flush @ index.js:83
r.sendPacket @ index.js:83
r.ping @ index.js:83
(anonymous) @ index.js:83
setTimeout (async)
r.setPing @ index.js:83
r.onPacket @ index.js:83
(anonymous) @ index.js:83
r.emit @ index.js:83
r.onPacket @ index.js:83
r.onData @ index.js:83
ws.onmessage @ index.js:83
@antoniab123456
Copy link

Experiencing absolutely the same issue and the code is correct. This only happens in Chrome for me. Mozilla is clean. In chrome, this err is raining over and over and duplicating all the chats I have. I tried using this method
socket.on('disconnect', () =>{ socket.disconnect(); });
It doesn't disconnect the client from the server. Repo in case needed https://github.com/antoniab123456/Chat_app

@danghung-dev
Copy link

danghung-dev commented Jun 13, 2018

the same problem here
image

browser is just idling, then the errors come up.

I used Chrome and mac OS

@antoniab123456
Copy link

Yes guys, can someone address that? Perhaps we need another line of code to fix that?

@dtroydev
Copy link

I have the same problem with socket.io and Chrome
Mac OS 10.13.5
Chrome Version 67.0.3396.87 (Official Build) (64-bit)
Node: 10.3.0
Express: 4.16.3
socket.io: 2.1.1

In Firefox everything is fine.

Error details below:

index.js:83 WebSocket is already in CLOSING or CLOSED state.
(anonymous) | @ | index.js:83
  | e.encodePacket | @ | index.js:83
  | (anonymous) | @ | index.js:83
  | r.write | @ | index.js:83
  | r.send | @ | index.js:83
  | r.flush | @ | index.js:83
  | r.sendPacket | @ | index.js:83
  | r.ping | @ | index.js:83
  | (anonymous) | @ | index.js:83
  | setTimeout (async) |   |  
  | r.setPing | @ | index.js:83
  | r.onPacket | @ | index.js:83
  | (anonymous) | @ | index.js:83
  | r.emit | @ | index.js:83
  | r.onPacket | @ | index.js:83
  | r.onData | @ | index.js:83
  | ws.onmessage | @ | index.js:83

When I click on the index.js:83 it takes me to this module:

/**
 * Expose `Backoff`.
 */

module.exports = Backoff;

/**
 * Initialize backoff timer with `opts`.
 *
 * - `min` initial timeout in milliseconds [100]
 * - `max` max timeout [10000]
 * - `jitter` [0]
 * - `factor` [2]
 *
 * @param {Object} opts
 * @api public
 */

function Backoff(opts) {
  opts = opts || {};
  this.ms = opts.min || 100;
  this.max = opts.max || 10000;
  this.factor = opts.factor || 2;
  this.jitter = opts.jitter > 0 && opts.jitter <= 1 ? opts.jitter : 0;
  this.attempts = 0;
}

/**
 * Return the backoff duration.
 *
 * @return {Number}
 * @api public
 */

Backoff.prototype.duration = function(){
  var ms = this.ms * Math.pow(this.factor, this.attempts++);
  if (this.jitter) {
    var rand =  Math.random();
    var deviation = Math.floor(rand * this.jitter * ms);
    ms = (Math.floor(rand * 10) & 1) == 0  ? ms - deviation : ms + deviation;
  }
  return Math.min(ms, this.max) | 0;
};

/**
 * Reset the number of attempts.
 *
 * @api public
 */

Backoff.prototype.reset = function(){
  this.attempts = 0;
};

/**
 * Set the minimum duration
 *
 * @api public
 */

Backoff.prototype.setMin = function(min){
  this.ms = min;
};

/**
 * Set the maximum duration
 *
 * @api public
 */

Backoff.prototype.setMax = function(max){
  this.max = max;
};

/**
 * Set the jitter
 *
 * @api public
 */

Backoff.prototype.setJitter = function(jitter){
  this.jitter = jitter;
};




//////////////////
// WEBPACK FOOTER
// ./~/backo2/index.js
// module id = 41
// module chunks = 0

Line 83 is:

this.jitter = jitter;

@ghost
Copy link

ghost commented Jul 2, 2018

I sort of have the same problem, with backo2.js and in line 83. here is my error:
index.js:83 Uncaught TypeError: Failed to execute 'readAsArrayBuffer' on 'FileReader': parameter 1 is not of type 'Blob'.
at n (index.js:83)
at n (index.js:83)
at n (index.js:83)
at n (index.js:83)
at n (index.js:83)
at n (index.js:83)
at Object.e.removeBlobs (index.js:83)
at s (index.js:83)
at r.encode (index.js:83)
at r.packet (index.js:83)
and dont worry about the other stuff, they all are in line 83, which is this.jitter = jitter;
It is annoying me so much as there isnt really any solution in google.

@garrrettt
Copy link

garrrettt commented Jul 2, 2018

Has anyone found a temporary solution? @antoniab123456 were you able to fix your chat app so that it doesn't duplicate your chats? It's doing the same thing to me.

Setup
OS: Mac OSX
Browser: Chrome 67.0.3396.99 (Official Build) (64-bit)
Node v10.5.0
Socket.io v2.1.1

@vkotu
Copy link

vkotu commented Jul 15, 2018

I am facing the exact problem @getify mentioned in chrome. Has any one found any work around for this?

@gavin310
Copy link

Same problem. Can someone please respond to this?

@amerej
Copy link

amerej commented Jul 18, 2018

Same problem.

Setup
OS: Mac OSX
browser: Chrome 67.0.3396.99 (Official Build) (64-bit), Node 9.6.1
socket.io version: 2.1.1

@ghost
Copy link

ghost commented Jul 20, 2018

@19smitgr idk what you mean by 'dulplicate chats' which sounds off topic which could be solved by broadcasting instead if you mean 'duplicate message in same screen'.

@garrrettt
Copy link

@Kino2007 I'm not making a chat application. I'm actually using websockets with a multiplayer game, and the websocket wasn't getting a disconnect message, so when the user disconnected and reconnected due to the random error that everyone is talking about, it would give the character a new socket ID, and the old sprite with the old socket ID wasn't deleted because it was just getting a "transport close" message instead of a "disconnect" message.

At this point, even if I get a "transport close" message, I just delete the user from my list of current users the same way as if I got a "disconnect" message.

@provokateurin
Copy link

provokateurin commented Jul 29, 2018

Same error here

After a quick search I found out that backo2 causes the error.

Then I used the dev version of socket.io and found out that the Socket.prototype.onevent throws the error.

@provokateurin
Copy link

I fixed it for me:

Previously I used this:

socket.on('ping', alert);

But then I changed it to this:

socket.on('ping', msg => {
    alert(msg);
});

And it worked!

@IainM22
Copy link

IainM22 commented Aug 22, 2018

I'm having the same problem. Any updates on this?

@Evgenyx82
Copy link

Same here.

socket.io & socket.io-client: "^2.1.1"
MacOS
Google Chrome is up to date
Version 68.0.3440.106 (Official Build) (64-bit)

@abhyuditjain
Copy link

I am having the same issue. Also, it's duplicating messages I send from server to client.

screen shot 2018-08-30 at 4 51 49 pm

MacOS: 10.13.6
Socket.io: "^2.1.1"
Chrome: 68.0.3440.106

@vkotu
Copy link

vkotu commented Aug 30, 2018

@abhyuditjain You can try reset all the listener's using socket.removeAllListeners(); to avoid multiple listener registrations which might cause the duplicating messages.

@Asher978
Copy link

Asher978 commented Sep 4, 2018

This seems to be an ongoing issue for several people and there has not been any solution to this. Any updates from anyone?

@abhyuditjain
Copy link

@vkotu If I remove all the listeners, then the socket doesn't reconnect to server on disconnect. Any way to fix this?

@antoniab123456
Copy link

just use websocket- node, it is easy to use and works without any errors and plus you don't need an external library for that:
https://codeburst.io/why-you-don-t-need-socket-io-6848f1c871cd
https://medium.com/@martin.sikora/node-js-websocket-simple-chat-tutorial-2def3a841b61
https://www.npmjs.com/package/websocket

@antoniab123456
Copy link

antoniab123456 commented Sep 5, 2018 via email

@cozuya
Copy link

cozuya commented Sep 20, 2018

Downgrading to 2.0.3 "fixed" this issue for me.

@Asher978
Copy link

@cozuya going to test it. Will let you know if it worked!

@Asher978
Copy link

Downgrading to 2.0.3 "fixed" this issue for me.

seems to be doing the trick. Tested for 4 mins and no errors. Let see how long it holds!

@cozuya thanks for posting this!

@icastillejogomez
Copy link

Same error in 2.1.1

Downgrading to 2.0.3 "fixed" this issue for me.

2.0.3 works for me! Thanks @cozuya

@wannymiarelli
Copy link

2.1.1 still facing the same issues. Tried to incrase ping and timeout but nothing changed

@rriixx
Copy link

rriixx commented Sep 27, 2018

having the same issue in chrome with v2.1.1, while browser is idling for a long time (about 20-30 minutes)

@Asher978
Copy link

2.1.1 still facing the same issues. Tried to incrase ping and timeout but nothing changed

downgrade to v 2.0.3

@cozuya
Copy link

cozuya commented May 15, 2019

You use Safari for localhost webdev? 🤔

@abhijitsdeshmukh
Copy link

You use Safari for localhost webdev? 🤔

Not really, but it is annoying with constant disconnects to test my app. This provides a temporary workaround till it is fixed in 3.0. This is not a solution. I posted if it will help somebody during dev.

@wumke
Copy link

wumke commented Jul 1, 2019

I'm using very low pingInterval and pingTimeouts to get realtime offline status changes, setting it to 30 seconds would break my flow... does anybody know a way to solve this issue so I can use the long timeout 'fix' for backgrounded browser tabs?

@deepinder10
Copy link

Any updates on this?

@jpsilvashy
Copy link

I'm using very low pingInterval and pingTimeouts to get realtime offline status changes, setting it to 30 seconds would break my flow... does anybody know a way to solve this issue so I can use the long timeout 'fix' for backgrounded browser tabs?

You should is the disconnect events on the socket to handle presence, when the socket disconnects, find the user and mark them as offline in your datastore

@jennifer-abu
Copy link

Here are the defaults from the documentation. Looks like a ping is sent every 25 seconds and each ping has 5 seconds to complete.

pingTimeout 5000 how many ms without a pong packet to consider the connection closed
pingInterval 25000 how many ms before sending a new ping packet

grahammcculloch added a commit to product-os/jellyfish that referenced this issue Mar 2, 2020
If the web socket is being flooded with 'update' messages, it doesn't ping quickly enough. The socket times out and closes down. It may re-open but some streamed updates may not make it to the client (UI). This can cause e2e stress tests to fail.

Ref: socketio/socket.io#3259 (comment)

Change-type: patch
Signed-off-by: Graham McCulloch <graham@balena.io>
@tudorpavel
Copy link

tudorpavel commented May 8, 2020

I noticed this same error but in a different context so I'm going to do a quick write-up with my problem and solution for anyone else ending up here.

My scenario was on Chrome for Android where the connection is dropped after locking the screen.

I'm using a token to identify the same user across different connections, which I send from client on connect using the query option approach and I update the token with a new one received from server:

// client
io('myAppUrl', {
  query: {
    token: localStorage.getItem('myKey') || ''
  }
});

io.on('reconnect_attempt', () => {
  io.opts.query = {
    token: localStorage.getItem('myKey') || ''
  };
});

io.on('my_custom_connection_successful_event', (token) => {
  localStorage.setItem('myKey', token);
});

The problem is that the Chrome for Android client would send to the server an empty string token on reconnect. Which I wouldn't expect since I added the 'reconnect_attempt' listener which makes sure to set the latest token from localStorage.

Well after a couple of hours of debugging with console logging all the possible client events I realized that the 'reconnect_attempt' event is not fired at all in my scenario of reconnecting. After unlocking the screen I get the following sequence of events:

  • disconnect (reason: 'transport close')
  • reconnecting (attempt: 1)
  • reconnect
  • ping/pong starts again

So no 'reconnect_attempt' event is being fired which explains why the token is still empty string (the initial value set on connect).

Long story short, the solution for me is to immediately update the io.opts.query instance variable on my custom "connection successful" event received form the server with the new token:

// client
io('myAppUrl', {
  query: {
    token: localStorage.getItem('myKey') || ''
  }
});

io.on('my_custom_connection_successful_event', (token) => {
  localStorage.setItem('myKey', token);
  io.opts.query = {
    token: token
  };
});

Now I understand that io.opts.query is an instance variable which is used on connect and all subsequent reconnects, so I can update it as soon as I want to. I don't have to wait for any reconnect-related event.

I feel a bit misled by the With query options docs. Maybe I misunderstood the use case in that example? But if it's similar to my use case, instead of the 'reconnect_attempt' example the docs could explain that io.opts.query is an instance variable that can be mutated and its current value is used on all subsequent reconnects. So it can be changed whenever you want to refresh the token, even on a 'refresh_token' custom event for example. I can do a PR if you think it's a good idea for improving the docs.

Edit:
Upon further investigation I realized my mistake was that the 'reconnect_attempt' listener was being removed from another part of my code... 🤦‍♂️ That's why I was not seeing it in my logging. So yes, the events sequence for my reconnect scenario is in fact:

  • disconnect (reason: 'transport close')
  • reconnect_attempt (attempt: 1)
  • reconnecting (attempt: 1)
  • reconnect
  • ping/pong starts again

Still, my realization that io.opts.query can be changed whenever you want is still valid. 😅

@AlanFonderflick
Copy link

Is it still ok to downgrade ? I switch on 2.0.3 but it doesn't seem to work.

@rithiksachdev
Copy link

Hello, I am facing the same kind of issue but with two errors. I tried @omardoma solution. Can anyone help?
Os - Ubuntu 18.04
Node - 12.16.2
npm - 6.14.5
socket.io - 2.3.0
my code -

app.js


const speech = require('@google-cloud/speech');
const speechClient = new speech.SpeechClient(); // Creates a client
const environmentVars = require('dotenv').config();
const io = require('socket.io')();
const http = require('http');
const server = http.createServer();
io.attach(server, {
  pingTimeout: 60000,
});



console.log(io)



// =========================== SOCKET.IO ================================ //

io.on('connection', function (client) {
  console.log('Client Connected to server');
  let recognizeStream = null;

ejs file

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js" integrity="sha256-bQmrZe4yPnQrLTY+1gYylfNMBuGfnT/HKsCGX+9Xuqo=" crossorigin="anonymous"></script>
<script src="/js/client.js"></script>

js file

const socket = io.connect("http://localhost:8080");

@teemasak
Copy link

teemasak commented Oct 6, 2020

I am using socket.io version 2.3.0. I use a websocket connection to client. When my client has a browser alert(which is synchronous) in UI and if user does not dismiss the alert for more than 30 sec(pingTimeout + pingInterval), I see the 'Websocket is already in CLOSING or CLOSED state' message and get a disconnect event with pingTimeout reason. I do not want this websocket to disconnect when UI has an alert. If I increase the pingTimeout to 10 min, the disconnect does not happen for 10 min. But from what I read in the comments above, this may have a negative impact(the client may not know there was a disconnect until 10min). But I noticed that if there is a network interruption etc., we get a disconnect with transport close reason. So is it okay to increase this pingTimeout+pingInterval? Is there a specific scenario when a disconnect is undetected? Or is there any other way to fix this?

emilyst added a commit to emilyst/thelounge that referenced this issue Mar 1, 2021
The default socket.io server-side ping timeout was changed from 60 seconds to 5 seconds. In browsers based on Chrome, this is not enough time to respond when the browser is idle. The end result is that the server sets the user away and then back approximately once every minute if the client window is idle, which is undesirable.

This change restores the previous timeout value.

See socketio/socket.io#3259 (comment).
@darrachequesne
Copy link
Member

For future readers: this should be fixed in latest versions due to two changes:

  • the heartbeat mechanism has been reversed in Socket.IO v3

The server now sends PING packets, so the client will not be declared dead when it does not send a PING in time due to timer throttling.

See also: https://socket.io/blog/engine-io-4-release/#heartbeat-mechanism-reversal

  • this fix, included in socket.io-client@^4.1.0

Please reopen if needed.

@darrachequesne darrachequesne added this to the 4.1.0 milestone Nov 19, 2021
@dcbarans
Copy link

dcbarans commented Dec 6, 2021

Wondering how complex this would be to fix in the 2.x client? We use the Java library https://github.com/mrniko/netty-socketio which only supports the 2.x protocol.

@darrachequesne
Copy link
Member

@dcbarans I'm afraid this is not possible, because the 2.x client relies on a timer to send PING packets...

@rohitnitk
Copy link

rohitnitk commented Mar 25, 2022

Hi, any update on this . whats the final solution?
I'm using "ws" in nodejs.

@shayan-ziaarabshahi
Copy link

shayan-ziaarabshahi commented Apr 29, 2023

I had this problem in socket.io and next.js. I solved this by below strategy:

const [reconnectSwitch, setReconnectSwitch] = useState()
useEffect(() => {
    !chatroomSocket.connected && chatroomSocket.connect()

    return () => {
        chatroomSocket.connected && chatroomSocket.disconnect()
        setReconnectSwitch(!reconnectSwitch)
    }
}, [reconnectSwitch])

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

No branches or pull requests