diff --git a/packages/xrpl/HISTORY.md b/packages/xrpl/HISTORY.md index ba53ba2a56..3ab430a68e 100644 --- a/packages/xrpl/HISTORY.md +++ b/packages/xrpl/HISTORY.md @@ -4,6 +4,9 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr ## Unreleased +### Fixed +* Fix event listener accumulation bug where `'connected'` event handlers would fire multiple times after each reconnection. The fix cleans up stale listeners from previous reconnect attempts to prevent duplicate event emissions on flaky connections with multiple sequential reconnect attempts. + ## 4.6.0 (2026-02-12) ### Added diff --git a/packages/xrpl/src/client/index.ts b/packages/xrpl/src/client/index.ts index bef7d06e76..a79f449041 100644 --- a/packages/xrpl/src/client/index.ts +++ b/packages/xrpl/src/client/index.ts @@ -262,8 +262,19 @@ class Client extends EventEmitter { this.emit('error', errorCode, errorMessage, data) }) + let connectedListener: (() => void) | undefined this.connection.on('reconnect', () => { - this.connection.on('connected', () => this.emit('connected')) + // Clean up any stale listener from previous reconnect attempt + // This prevents duplicate event emissions when multiple reconnect attempts + // occur before a successful connection (e.g., on flaky networks) + if (connectedListener !== undefined) { + this.connection.off('connected', connectedListener) + } + + connectedListener = (): boolean => this.emit('connected') + // Use .once() so the listener auto-removes after firing + // This prevents listener accumulation across reconnections + this.connection.once('connected', connectedListener) }) this.connection.on('disconnected', (code: number) => {