Skip to content

Commit 7dbf92c

Browse files
fix: keep WebSocket binaryType + close handling stable across compat dates
Three related WebSocket fixes so PartyServer behaves identically on every Worker compatibility date, plus the tooling to prove it. All changes are no-ops on older dates and corrective on newer ones (backward compatible). 1. Pin binaryType to "arraybuffer" for non-hibernating connections. On compat dates >= 2026-03-17 the `websocket_standard_binary_type` flag flips the default server-side `binaryType` from "arraybuffer" to "blob", so binary frames arrived as `Blob` instead of `ArrayBuffer` on the in-memory accept path. Every PartyServer consumer (and frameworks built on it, e.g. Cloudflare Agents / @cloudflare/voice) has always received `ArrayBuffer`, so it is pinned back in `InMemoryConnectionManager.accept`. The Hibernation API is unaffected (it always delivers `ArrayBuffer`). 2. Accept non-hibernating connections in half-open mode. `accept({ allowHalfOpen: true })` (with a try/catch fallback to bare accept() for older runtimes) keeps PartyServer's manual close handshake in control on compat dates >= 2026-04-07, where `web_socket_auto_reply_to_close` would otherwise auto-tear-down a tunneled DO socket and surface a spurious retryable "Network connection lost." rejection. 3. Stop reporting transport-teardown errors via onError. A retryable "Network connection lost." / "WebSocket peer disconnected" error that fires on an already CLOSING/CLOSED connection is the socket going away during the close handshake, not an application error. New `transport-errors.ts` (`isBenignTeardownError`, structured `retryable`-first detection so it stays correct under `enhanced-error-serialization` >= 2026-04-21) suppresses it in both the in-memory `handleErrorFromClient` and hibernating `webSocketError` paths. Genuine mid-connection (OPEN) errors still reach onError. Verification: - Bumped wrangler ^4.86.0 -> ^4.100.0 so local `wrangler dev` runs compat dates through 2026-06-11 (older workerd capped at 2026-05-03). - New `BinaryTypeProbe` DO + `compat-binarytype.test.ts` + parametrized `vitest.compat.config.ts` lock the ArrayBuffer contract across dates; the `test:compat-matrix` script runs it at 2026-01-28 / 03-24 / 04-07 / 04-21 and is wired into PR CI. The gate is non-vacuous (reports "blob" without the pin, "arraybuffer" with it). - 6 new `transport-errors.test.ts` unit tests cover the suppression predicate. - Full suite: 86 unit tests pass; typecheck, lint, format clean. Changeset: patch -> 0.5.7. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent aeba44f commit 7dbf92c

16 files changed

Lines changed: 559 additions & 24 deletions
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
"partyserver": patch
3+
---
4+
5+
Accept non-hibernating WebSocket connections in half-open mode (`accept({ allowHalfOpen: true })`).
6+
7+
On compatibility dates `>= 2026-04-07` the `web_socket_auto_reply_to_close` flag makes the runtime send a reciprocal Close frame and tear the socket down automatically. For a non-hibernating PartyServer (`hibernate: false`), the Durable Object sits on the server end of a connection that the runtime tunnels back to the client, so that auto-teardown could fire through an already-severed tunnel — surfacing as a spurious retryable `Network connection lost.` rejection (for example when a Durable Object is reset while a connection is still open). Half-open mode keeps PartyServer's existing close handling in control; it already reciprocates the peer's Close frame on every compatibility date, so client behavior is unchanged.
8+
9+
Also in this release, two related WebSocket fixes that keep behavior consistent across all compatibility dates:
10+
11+
- **Pin `binaryType` to `"arraybuffer"` for non-hibernating connections.** On compatibility dates `>= 2026-03-17` the `websocket_standard_binary_type` flag flips the default server-side `binaryType` from `"arraybuffer"` to `"blob"`, so binary frames arrived as `Blob` instead of `ArrayBuffer` on the in-memory path. PartyServer (and frameworks built on it, e.g. Cloudflare Agents) have always received `ArrayBuffer`, so it is now pinned back in `accept()`. This is a no-op on older dates and corrective on newer ones; the Hibernation API is unaffected (it always delivers `ArrayBuffer`).
12+
- **Stop reporting transport-teardown errors as `onError`.** A retryable `Network connection lost.` / `WebSocket peer disconnected` error that fires on an already closing/closed connection is the socket going away during the close handshake, not an application error. It is now suppressed when the connection is `CLOSING`/`CLOSED` (detected via the structured `retryable` flag, with a message fallback), so it no longer spams logs on abrupt client disconnects. Genuine mid-connection (`OPEN`) errors still reach `onError`.

‎.github/workflows/pullrequest.yml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,6 @@ jobs:
2525
- run: npm ci
2626
- run: npm run build
2727
- run: npm run check
28+
# Compat-matrix: lock the WebSocket binaryType contract across the
29+
# compatibility-date flag transitions (websocket_standard_binary_type).
30+
- run: npm run test:compat-matrix -w partyserver

‎.gitignore‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,6 @@ dist
135135
.DS_Store
136136
.env
137137
.dev.vars
138+
139+
# Temporary compat-verification harness (Layer A)
140+
.compat-harness/

‎fixtures/alarm-restart-e2e/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@
1616
"@cloudflare/vite-plugin": "^1.34.0",
1717
"@cloudflare/workers-types": "^4.20260426.1",
1818
"vite": "^8.0.10",
19-
"wrangler": "^4.86.0"
19+
"wrangler": "^4.100.0"
2020
}
2121
}

‎package-lock.json‎

Lines changed: 268 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)