partyserver: with hibernation off, a closing socket removes a newer connection with the same id from getConnections(), so that client stops receiving broadcasts

Open Beginner friendly
#430 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
typescript
Domain
backend

Research direction

Start with InMemoryConnectionManager.accept and removeConnection, where the issue identifies the map entry and close/error listener. Check the tests for connection replacement and delayed close events, then verify that closing an older socket leaves the newer connection available through getConnections(). Done means the replacement connection continues receiving broadcasts after the old socket closes.

Written by the indexing model from the issue text.

Description

InMemoryConnectionManager.accept stores connections in a Map keyed by connection.id. Its close/error listener runs this.#connections.delete(connection.id) without checking that the entry is still that connection.

y-partyserver's YProvider sends one _pk (from connectionId, or nanoid(10) once per provider) on every reconnect. So a client that reconnects while the server still holds its old socket replaces the map entry. When the old socket's close arrives later, the delete removes the new, live connection. From then on getConnections() never yields it. The client keeps sending and the server keeps applying its updates, but no broadcast reaches it: with y-partyserver that is every document update and every awareness change from the other peers. Nothing tells the client, and the provider has no watchdog for a silent socket, so it stays deaf until it reconnects for some other reason.

Repro (partyserver 0.5.10, y-partyserver 2.2.0, wrangler 4.159.0, compatibility date 2026-11-01, hibernate: false, via unstable_startWorker). A and B are YProviders in one room:

  1. Open a raw WebSocket to the room with ?_pk=<A.provider.id>. It stands in for an old socket the server has not seen die yet.
  2. Connect A, then terminate the raw socket and wait 1.5 s.
  3. A's updates still reach B. But within 3 s, none of these reach A: B's updates, B's awareness change, and the updates of a newcomer C.

The result is the same when the raw socket opens after A. Without the raw socket, all of these checks pass.

Suggested fix: in removeConnection, delete only when this.#connections.get(connection.id) === connection. The older socket then leaves the map as soon as it is replaced, which fits: it is the stale one.

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.