partysocket: a room name with ? or # is not encoded in the URL

Open Beginner friendly
#443 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
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.

https://github.com/cloudflare/partykit/blob/partysocket%401.3.0/packages/partysocket/src/index.ts#L107

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

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.