perspective-dev/perspective · warning · Error
WebSocket message dropped
Error message
WebSocket message dropped (${ws.readyState}) What it means
The perspective WebSocket client throws this when `send_message` is called while the socket is still CONNECTING (readyState 0) — the connection has not finished opening, so the outgoing message is dropped rather than sent. Unlike the transport error, `handle_error`/reconnect is not invoked; the message is simply discarded and the error thrown to the caller.
Solutions
- Await the client's connection promise (or the WebSocket `open` event) before calling send_message or any API that sends.
- Gate sends on readyState: wait/poll until `ws.readyState === WebSocket.OPEN`.
- Serialize sends behind the connection lifecycle: queue messages raised while CONNECTING and flush them on `open`.
- In reconnect logic, ensure in-flight/pending requests are re-attached to the newly opened socket instead of the old one.
Example fix
// before
const client = websocket("ws://host/perspective");
client.send_message(openTableReq); // socket still CONNECTING -> dropped
// after
const client = await websocket("ws://host/perspective"); // await connection
// or: await new Promise(r => client.ws.addEventListener("open", r));
client.send_message(openTableReq); Defensive patterns
Strategy: retry
Validate before calling
async function waitForOpen(ws, timeoutMs = 10000) {
if (ws.readyState === WebSocket.OPEN) return;
await new Promise((resolve, reject) => {
const t = setTimeout(() => reject(new Error("connect timeout")), timeoutMs);
ws.addEventListener("open", () => { clearTimeout(t); resolve(); }, { once: true });
ws.addEventListener("error", () => { clearTimeout(t); reject(new Error("connect failed")); }, { once: true });
});
} Type guard
function isOpen(ws: WebSocket): boolean {
return ws.readyState === WebSocket.OPEN;
} Try / catch
try {
client.send_message(msg);
} catch (e) {
if (/^WebSocket message dropped \(0\)/.test(e.message)) {
await waitForOpen(client.ws);
client.send_message(msg); // retry once open
} else throw e;
} Prevention
- Never fire requests before awaiting the client's connection promise or the `open` event.
- Queue messages emitted during CONNECTING and flush them on `open`.
- Initialize the perspective client once at app startup and share the awaited instance.
- In reconnect flows, route all senders through a single gate that waits for OPEN.
When it happens
Trigger: Calling `send_message` (directly or via a higher-level API) between initiating the WebSocket connection and receiving its `open` event — e.g. sending immediately after constructing the client without awaiting connection, or racing requests against a reconnect in progress.
Common situations: Not awaiting the promise/`open` event from `websocket()` before issuing table/view commands; firing requests in a framework mount lifecycle before the async connection resolves; reconnect logic that recreates the socket while pending calls still target the new CONNECTING socket.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
AI-assisted analysis of perspective-dev/perspective@11c8238c0c (2026-09-09).
Data as JSON: /api/errors/ce9c6093de5b3893.
Report an issue: GitHub.
Appendix: source
Thrown at rust/perspective-js/src/ts/websocket.ts:80
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)