partysocket: failed connections are reported as a clean close with code 1000, so `shouldReconnectOnClose` stops reconnecting

Open Beginner friendly
#428 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
82/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
node.js, typescript

Research direction

Start with packages/partysocket/src/ws.ts, especially _handleError and _disconnect, which the issue identifies as the likely source of the synthetic close event. Compare the dispatched close event with the underlying WebSocket's close event for failed connections and dropped connections. Add or update tests for those cases; done means the event reports the underlying failure code and shouldReconnectOnClose allows retries.

Written by the indexing model from the issue text.

Description

When a connection attempt fails or an open connection drops (server down, network lost), PartySocket dispatches a close event with code: 1000, wasClean: true and reason: "unknown reason", and dispatches it before the error event. The underlying WebSocket reports 1006 / wasClean: false. Because the same synthetic event is passed to the new shouldReconnectOnClose option, a predicate like e => e.code !== 1000 ("reconnect unless the server closed normally") turns reconnection off the first time the server is unreachable.

Reproduction (npm i [email protected], Node 26, nothing listening on port 9):

// repro.mjs
import WebSocket from "partysocket/ws";

// Nothing listens on this port, so every attempt fails.
const ws = new WebSocket("ws://127.0.0.1:9", undefined, {
  minReconnectionDelay: 100,
  // Reconnect unless the server closed normally.
  shouldReconnectOnClose: (e) => e.code !== 1000,
});
ws.addEventListener("close", (e) => console.log("close", e.code, e.wasClean, e.reason));
ws.addEventListener("error", () => console.log("error"));
setTimeout(() => {
  console.log("shouldReconnect", ws.shouldReconnect, "retryCount", ws.retryCount);
  process.exit(0);
}, 2000);

node repro.mjs

Actual:

close 1000 true unknown reason
error
shouldReconnect false retryCount 0

Expected: the close event carries the real code (close 1006 false, which is what new WebSocket("ws://127.0.0.1:9") itself reports), the predicate returns true, and the socket keeps retrying.

The same happens when an established connection is dropped without a close frame (server process killed): the user sees close 1000 true and no reconnect.

Tested with partysocket 1.3.0 and main (cloudflare/partykit f0a2e97d; packages/partysocket/src/ws.ts last changed in 6b977426), on Node 24.22.2, Node 26.13.0 and Bun 1.4.4.

Likely cause: _handleError calls this._disconnect(undefined, ...), so _disconnect(code = 1000, reason) synthesizes new CloseEvent(1000, ...) and removes the listeners, so the real close event from the socket is never seen.

Credit: https://ucsbplse.github.io/

Dominant language
TypeScript
Stars
1.3k
Forks
86
PR merge metrics
No merged PRs in 30d

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from cloudflare/partykit

All issues in cloudflare/partykit

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.