Bug eventemitter2.js - EventEmitter.prototype.off

  1. FXServer version 5848
  2. Connecting to an external (same system) data source through websockets (socket.io)
  3. When server side of socket.io (running on NestJS) breaks connection EventEmitter.prototype.off errors out when it’s not needing to error
  4. Server side bug (artifacts)
  5. Create a socket.io server, and a FiveM resource that connects to it.

On line 590 of citizen/scripting/v8/eventemitter2.js it will error out when socket.io disconnects, the prototype seems to assume that all types are ‘removeListener’, while the one I’m triggering is ‘offline’.

When I manually adjust the code to (see code block), the error fixes itself and expected behavior returns.

  EventEmitter.prototype.off = function(type, listener) {
-    if (typeof listener !== 'function') {
+    if (type === 'removeListener' && typeof listener !== 'function') {
      throw new Error('removeListener only takes instances of Function');
    }
  ...

type seems to always be a string and based on the code within the prototype removeListener is the reason the prototype is wanting to throw an error.

If there’s a public repo somewhere I am willing to make a PR, else, this should be the fix based on reading just this method in isolation.


Edit: Other solution is to just return void if type is ‘offline’

  EventEmitter.prototype.off = function(type, listener) {
+    if(type === 'offline') {
+      return;
+    }

    if (typeof listener !== 'function') {
      throw new Error('removeListener only takes instances of Function');
    }

Edit 2:

While both solutions work, neither are a permanent solution. On repeated server side (NestJS) disconnects and reconnects, some of the socket.io events (only on ‘connect’ during limited testing) gets additional events added; causing it to fire several times (n+1)

Reproduction steps could help with investigating here.

Also, tagging @pink-pepper for visibility as I believe they may know more about this lib.

Thanks d-bubble.

I’ve made a repo with the basic code, this is enough to reproduce it on my end.

The socket-server folder is a very basic express based socket.io server.
And within the fivem-resource/websocket folder is a very basic resource to connect to it.

Socket server uses yarn as package manager, but npm should work fine.

Steps Socket side:

  • Install node_modules with yarn
  • Start the server with yarn start

Steps FiveM side:

  • Copy the websocket folder to somewhere in your fivem resources folder.
  • Start up your server and ensure websocket

At this point in time, you should see a Connected to server message on the FiveM server console.
And a user connected on the socket server console.

Close the socket server (CTRL+C), this should break the connection and the error appears on the FiveM server console.

[    script:websocket] Uncaught Error: removeListener only takes instances of Function
[    script:websocket] citizen:/scripting/v8/eventemitter2.js(591,13): EventEmitter.off
[    script:websocket] webpack://@reddev-fivem/websocket/./node_modules/socket.io-client/node_modules/engine.io-client/lib/socket.js?(625,9): onClose
[    script:websocket] webpack://@reddev-fivem/websocket/./node_modules/socket.io-client/node_modules/engine.io-client/lib/socket.js?(220,14): eval
[    script:websocket] webpack://@reddev-fivem/websocket/./node_modules/component-emitter/index.js?(145,20): Emitter.emit
[    script:websocket] webpack://@reddev-fivem/websocket/./node_modules/socket.io-client/node_modules/engine.io-client/lib/transport.js?(115,10): onClose
[    script:websocket] webpack://@reddev-fivem/websocket/./node_modules/socket.io-client/node_modules/engine.io-client/lib/transports/websocket.js?(180,33): onClose
[    script:websocket] webpack://@reddev-fivem/websocket/./node_modules/ws/lib/event-target.js?(136,16): onClose
[    script:websocket] node:events(394,28): emit
[    script:websocket] webpack://@reddev-fivem/websocket/./node_modules/ws/lib/websocket.js?(198,10): emitClose
[    script:websocket] webpack://@reddev-fivem/websocket/./node_modules/ws/lib/websocket.js?(824,20): receiverOnFinish
[    script:websocket] 
[    script:websocket] Error: removeListener only takes instances of Function

Tried testing it within NestJS and Express on socket server side, same error on both.
Hence I ruled out the external server itself as an issue.

Adding the ‘fix’ from above, things do work as intended, no error and the websocket connection reconnects once the socket server is back online.
However the ‘connect’ listener becomes double defined (n+1). I’m assuming other listeners will have the same issue.

In practice, this isn’t an issue for me as I don’t expect the socket server to go offline a lot between scheduled restarts, but it’s not the proper permanent solution.

Did you ever get around fixing this yourself or is this still an issue? Experiencing the same error message myself with a simple socket.io-client connection.

hello ? is there new update

Just for the record, I am too experiencing this issue!

Reviving this topic since I experiencing the same issue and while the proposed fixes work, Im unsure about side effects.

import { io } from 'socket.io-client';

const socket = io('http://localhost:3001', {
    autoConnect: false
})

socket.connect()

As far as code, this is enough to cause the error when it fails to connect

Yea, I took a break for about a year from FiveM.
And was surprised it wasn’t even addressed yet.

Currently, I’m using my proposed solution, but as I said in the original message from Sep 2022.
It’s not a permanent solution. It gets rid of the message but does not solve the underlying issue.

Hopefully, now several people have revived the thread, core devs will do something with it.