From 5383192e407a66edd85540874d97de82898e8be9 Mon Sep 17 00:00:00 2001 From: Sunil Pai Date: Mon, 9 Feb 2026 03:18:50 +0000 Subject: [PATCH] Release _connectLock when maxRetries reached Fix a bug where reconnect() could not start a new connection after maxRetries was exhausted because _connectLock was not released in _connect(). Set _connectLock = false on the early return when max retries are reached. Added a test (reconnecting.test.ts) that reproduces the issue and verifies reconnect() works after retries are exhausted, and included a changeset entry documenting the patch. --- .changeset/fix-reconnect-after-max-retries.md | 5 +++ .../src/tests/reconnecting.test.ts | 37 +++++++++++++++++++ packages/partysocket/src/ws.ts | 1 + 3 files changed, 43 insertions(+) create mode 100644 .changeset/fix-reconnect-after-max-retries.md diff --git a/.changeset/fix-reconnect-after-max-retries.md b/.changeset/fix-reconnect-after-max-retries.md new file mode 100644 index 0000000..8b4f4d0 --- /dev/null +++ b/.changeset/fix-reconnect-after-max-retries.md @@ -0,0 +1,5 @@ +--- +"partysocket": patch +--- + +Fix `reconnect()` not working after `maxRetries` has been exhausted. The `_connectLock` was not released when the max retries early return was hit in `_connect()`, preventing any subsequent `reconnect()` call from initiating a new connection. diff --git a/packages/partysocket/src/tests/reconnecting.test.ts b/packages/partysocket/src/tests/reconnecting.test.ts index 723ce7e..8672683 100644 --- a/packages/partysocket/src/tests/reconnecting.test.ts +++ b/packages/partysocket/src/tests/reconnecting.test.ts @@ -937,3 +937,40 @@ testDone("reconnect after closing", (done, fail) => { } }); }); + +testDone( + "reconnect() works after maxRetries has been exhausted", + (done, fail) => { + // Connect to an unreachable URL with maxRetries=0 so retries exhaust quickly. + // This reproduces the bug where _connectLock was not released when + // maxRetries was reached, preventing reconnect() from working. + // (https://github.com/cloudflare/partykit/issues/252) + const ws = new ReconnectingWebSocket(ERROR_URL, undefined, { + maxRetries: 0, + connectionTimeout: 500, + minReconnectionDelay: 10, + maxReconnectionDelay: 50 + }); + + let reconnected = false; + ws.addEventListener("error", () => { + if (reconnected) return; + reconnected = true; + + // maxRetries is now exhausted. Switch to the working server and reconnect. + // @ts-expect-error accessing private _url for testing + ws._url = URL; + ws.reconnect(); + }); + + ws.addEventListener("open", () => { + ws.close(); + done(); + }); + + setTimeout(() => { + ws.close(); + fail(new Error("timed out waiting for reconnect after maxRetries")); + }, 10000); + } +); diff --git a/packages/partysocket/src/ws.ts b/packages/partysocket/src/ws.ts index 69d316d..735e792 100644 --- a/packages/partysocket/src/ws.ts +++ b/packages/partysocket/src/ws.ts @@ -455,6 +455,7 @@ export default class ReconnectingWebSocket extends (EventTarget as TypedEventTar if (this._retryCount >= maxRetries) { this._debug("max retries reached", this._retryCount, ">=", maxRetries); + this._connectLock = false; return; }