partysocket: a room name with ? or # is not encoded in the URL
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 68/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- nodejs, typescript
- Domain
- networking
Research direction
The URL is built in getPartyInfo() in packages/partysocket/src/index.ts, around line 107, where the party and room names are inserted without encoding. Start there, then run the repro script in the issue against the server it creates to confirm the ? and # behaviour. Done means room a?b reaches the server as /parties/main/a%3Fb with the query string intact, and room a#b connects, with a regression test added to the partysocket suite.
Written by the indexing model from the issue text.
Description
getPartyInfo() puts the party and room names into the URL as they are. A ? in a name starts the query string, so the server gets another room name and the rest of the name as a query. A # starts a fragment, and the WebSocket constructor rejects a URL with a fragment, so the socket never connects.
Repro
The packages: [email protected], [email protected]. Install them with npm i [email protected] [email protected].
// npm install, then: node repro.ts (Node.js 22.18 or later runs TypeScript directly)
import type { AddressInfo } from "node:net";
import PartySocket from "partysocket";
import { WebSocketServer } from "ws";
const wss = new WebSocketServer({ host: "127.0.0.1", port: 0 });
await new Promise((resolve) => wss.once("listening", resolve));
wss.on("connection", (_client, req) => {
console.log("the server got the URL:", req.url);
});
const host = `127.0.0.1:${(wss.address() as AddressInfo).port}`;
// room "a?b"
const a = new PartySocket({ host, room: "a?b", id: "client-1" });
await new Promise((resolve) => setTimeout(resolve, 300));
a.close();
// room "a#b"
const b = new PartySocket({ host, room: "a#b", minReconnectionDelay: 50, maxReconnectionDelay: 50 });
let errors = 0;
let firstError: string | undefined;
b.addEventListener("error", (e) => {
errors++;
firstError ??= e.message;
});
await new Promise((resolve) => setTimeout(resolve, 500));
console.log(`room "a#b": ${errors} error events in 500 ms, the first: ${firstError}`);
b.close();
for (const client of wss.clients) client.terminate();
wss.close();
node repro.ts (Node.js 22.18 or later runs TypeScript directly)
Actual
the server got the URL: /parties/main/a?b?_pk=client-1
room "a#b": 10 error events in 500 ms, the first: hash
Expected
The room name stays in the path of the URL, for example /parties/main/a%3Fb?_pk=client-1, and a room name with # connects.
A fix
A fix with a new test is on the branch fix/partysocket-encode-room-name of my fork, at 720a46c. On a GitHub Actions runner, the new test fails before the change and passes after it, and the whole partysocket suite passes. This repository accepts pull requests only from collaborators, so I did not open a pull request. I can open one if you allow it, or you can take the commit.
I found this with AI assistance (Claude) during a review of WebSocket client libraries. The repro above was run with partysocket 1.3.0 on Node.js 24.22.2 on Linux (a GitHub Actions runner), and the output above is from that run. The fix was also prepared with AI assistance (Claude).
- Dominant language
- TypeScript
- Stars
- 1.3k
- Forks
- 86
- PR merge metrics
- No merged PRs in 30d
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from cloudflare/partykit
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
cloudflare/partykit#444 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
cloudflare/partykit#434 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
cloudflare/partykit#430 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 69/100
cloudflare/partykit#429 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
cloudflare/partykit#428 ·
All issues in cloudflare/partykit
Similar issues
-
bug parity
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
WenyuChiou/agent-board#1489 ·
Maintainers usually reply within 1 day
-
area: bazaar bug complexity: medium
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
accensa/x402-facilitator-stellar#779 ·
Maintainers usually reply within 1 day
-
bug good first issue P4 size:S web-ui
Difficulty 2/5 Half a day Newbie friendliness 85/100
josephfung/curia#2600 ·
Maintainers usually reply within 1 day