From 3442918dc60922ff055da89d80b556c4cc18ff8c Mon Sep 17 00:00:00 2001 From: Kuan Lin Date: Sat, 21 Feb 2026 00:15:16 -0500 Subject: [PATCH 1/2] prevent 'connected' event listener accumulation on reconnect by using .once() instead of .on() --- packages/xrpl/HISTORY.md | 3 +++ packages/xrpl/src/client/index.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/xrpl/HISTORY.md b/packages/xrpl/HISTORY.md index ba53ba2a56..05492fc5a6 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. Changed `.on()` to `.once()` in the Client constructor's reconnect handler to prevent listener accumulation. + ## 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..79b0d4b9e8 100644 --- a/packages/xrpl/src/client/index.ts +++ b/packages/xrpl/src/client/index.ts @@ -263,7 +263,7 @@ class Client extends EventEmitter { }) this.connection.on('reconnect', () => { - this.connection.on('connected', () => this.emit('connected')) + this.connection.once('connected', () => this.emit('connected')) }) this.connection.on('disconnected', (code: number) => { From 4aeae982bf1740c59d91a6d152f05c83d09cb1ec Mon Sep 17 00:00:00 2001 From: Kuan Lin Date: Sat, 21 Feb 2026 01:09:42 -0500 Subject: [PATCH 2/2] prevent duplicate event emissions on flaky connections with multiple sequential reconnect attempts --- packages/xrpl/HISTORY.md | 2 +- packages/xrpl/src/client/index.ts | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/xrpl/HISTORY.md b/packages/xrpl/HISTORY.md index 05492fc5a6..3ab430a68e 100644 --- a/packages/xrpl/HISTORY.md +++ b/packages/xrpl/HISTORY.md @@ -5,7 +5,7 @@ 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. Changed `.on()` to `.once()` in the Client constructor's reconnect handler to prevent listener accumulation. +* 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) diff --git a/packages/xrpl/src/client/index.ts b/packages/xrpl/src/client/index.ts index 79b0d4b9e8..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.once('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) => {