authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-10-03 22:40:57-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-03 22:40:57-07:00
loga92c43b80ba307f112e0ae03bf64d841475cf326
treec4b7f5f6cc87315a9b264f7d00724d6c642657f6
parentbf5c146be914c9b596cdc45d343228271115c099
signature Signed by PGP key B5690EEEBB952194

fix(client-runtime): relay disconnects no longer show as thread errors (#15470)

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

2 files changed, 25 insertions(+), 3 deletions(-)

packages/client-runtime/src/errors/transport.test.ts+15
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1import { describe, expect, it } from "vite-plus/test";1import { describe, expect, it } from "vite-plus/test";
22
3import { NETWORK_BLOCKING_HINT } from "./network.ts";
3import { isTransportConnectionErrorMessage, sanitizeThreadErrorMessage } from "./transport.ts";4import { isTransportConnectionErrorMessage, sanitizeThreadErrorMessage } from "./transport.ts";
45
5describe("isTransportConnectionErrorMessage", () => {6describe("isTransportConnectionErrorMessage", () => {
...@@ -30,6 +31,20 @@ describe("isTransportConnectionErrorMessage", () => {...@@ -30,6 +31,20 @@ describe("isTransportConnectionErrorMessage", () => {
30 expect(isTransportConnectionErrorMessage("ClientProtocolError: socket closed")).toBe(true);31 expect(isTransportConnectionErrorMessage("ClientProtocolError: socket closed")).toBe(true);
31 });32 });
3233
34 it("recognizes relay connection errors that carry the network hint", () => {
35 for (const sentence of [
36 "Relay environment disconnected.",
37 "Relay environment could not establish a WebSocket connection.",
38 ]) {
39 expect(isTransportConnectionErrorMessage(`${sentence} ${NETWORK_BLOCKING_HINT}`)).toBe(true);
40 }
41 expect(
42 isTransportConnectionErrorMessage(
43 "Your ChatGPT connection expired or was disconnected. Sign in again.",
44 ),
45 ).toBe(false);
46 });
47
33 it("returns true for the T3 server WebSocket message", () => {48 it("returns true for the T3 server WebSocket message", () => {
34 expect(isTransportConnectionErrorMessage("Unable to connect to the T3 server WebSocket.")).toBe(49 expect(isTransportConnectionErrorMessage("Unable to connect to the T3 server WebSocket.")).toBe(
35 true,50 true,
packages/client-runtime/src/errors/transport.ts+10-3
...@@ -1,11 +1,18 @@...@@ -1,11 +1,18 @@
1import { NETWORK_BLOCKING_HINT } from "./network.ts";
2
3const escapeRegExp = (text: string) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4
1const TRANSPORT_ERROR_PATTERNS = [5const TRANSPORT_ERROR_PATTERNS = [
2 /\bSocketCloseError\b/i,6 /\bSocketCloseError\b/i,
3 /\bSocketOpenError\b/i,7 /\bSocketOpenError\b/i,
4 /\bSocket is not connected\b/i,8 /\bSocket is not connected\b/i,
5 /Unable to connect to the T3 server WebSocket\./i,9 /Unable to connect to the T3 server WebSocket\./i,
6 /\bis not connected\.$/i,10 // The RPC session appends the network hint for relay connections. Any other
7 /\bdisconnected\.$/i,11 // trailing text means a different error that the user should still see.
8 /\bcould not establish a WebSocket connection\.$/i,12 new RegExp(
13 `\\b(?:is not connected|disconnected|could not establish a WebSocket connection)\\.(?: ${escapeRegExp(NETWORK_BLOCKING_HINT)})?$`,
14 "i",
15 ),
9 /\bClientProtocolError\b/i,16 /\bClientProtocolError\b/i,
10 /\bRpcClientError\b/i,17 /\bRpcClientError\b/i,
11 /\bping timeout\b/i,18 /\bping timeout\b/i,