perspective-dev/perspective · error · Error

WebSocket transport error

Error message

WebSocket transport error (${ws.readyState})

What it means

The perspective WebSocket client throws this when `send_message` is called while the underlying WebSocket is in the CLOSING (2) or CLOSED (3) readyState — the message cannot be transported. The error is first reported to the client via `client.handle_error` (which may trigger a reconnect) and then thrown to the caller.

Solutions

  1. Check `client.is_connected()` / the WebSocket readyState before sending, and skip or queue messages when not open.
  2. Await connection before sending: wrap sends in the client's connect/open promise (e.g. `await client.connect()` or the socket's `open` event).
  3. Add a retry wrapper that listens for the reconnection attempt triggered by handle_error and resends queued messages once readyState is OPEN.
  4. Verify the server/proxy keepalive configuration (timeouts, ping intervals) if closures happen during idle periods.

Example fix

// before
const client = await websocket("ws://host/perspective");
setInterval(() => client.send_message(makeRequest()), 1000); // throws after socket closes

// after
const client = await websocket("ws://host/perspective");
async function safeSend(req) {
  if (client.ws.readyState !== WebSocket.OPEN) {
    await reconnectAndWait(client);
  }
  try {
    client.send_message(req);
  } catch (e) {
    if (String(e.message).startsWith("WebSocket transport error")) {
      await reconnectAndWait(client);
      client.send_message(req);
    } else throw e;
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

function canSend(client) {
  return client.ws && client.ws.readyState === WebSocket.OPEN;
}
if (!canSend(client)) await reconnectAndWait(client);

Type guard

function isSocketOpen(ws: WebSocket | undefined): ws is WebSocket & { readyState: typeof WebSocket.OPEN } {
  return !!ws && ws.readyState === WebSocket.OPEN;
}

Try / catch

try {
  client.send_message(msg);
} catch (e) {
  if (/^WebSocket transport error \((2|3)\)/.test(e.message)) {
    await reconnect(client);
    client.send_message(msg); // resend after reconnect
  } else throw e;
}

Prevention

When it happens

Trigger: Calling `client.send_message(...)` (or any higher-level API that sends, e.g. table/view requests) after the WebSocket has closed — server shutdown, network drop, explicit `close()`, or idle/keepalive failure — before the reconnect completes.

Common situations: Server restart or proxy timeout closing the socket while the app continues issuing queries; calling send during a reconnect window; forgetting to await an initial connection promise; firing requests after `client.close()` was called.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of perspective-dev/perspective@11c8238c0c (2026-09-09). Data as JSON: /api/errors/64ae4fd551de8346. Report an issue: GitHub.

Appendix: source

Thrown at rust/perspective-js/src/ts/websocket.ts:77

            client.handle_error(msg, connect);
            reject(msg);
        };

        ws.onmessage = (event) => {
            client.handle_response(event.data);
        };

        await receiver;
    }

    async function send_message(proto: Uint8Array) {
        if (
            ws.readyState === WebSocket.CLOSING ||
            ws.readyState === WebSocket.CLOSED
        ) {
            const msg = `WebSocket transport error (${ws.readyState})`;
            client.handle_error(msg, connect);
            throw new Error(msg);
        } else if (ws.readyState === WebSocket.CONNECTING) {
            const msg = `WebSocket message dropped (${ws.readyState})`;
            throw new Error(msg);
        } else {
            const buffer = proto.slice().buffer;
            ws.send(buffer);
        }
    }

    async function on_close() {
        console.debug("Closing WebSocket");
        ws.close();
    }

    client = new Client(send_message, on_close);
    await connect();
    return client;
}

View on GitHub (pinned to 11c8238c0c)