karatelabs/karate · error · WsException
CONNECTION_CLOSED
CONNECTION_CLOSED
Error message
websocket is not open
What it means
WsClient.send(WsFrame) first checks isOpen() and throws a WsException of type CONNECTION_CLOSED if the underlying Netty channel is not active. The library refuses to send on a dead/closed connection rather than silently queueing or failing asynchronously. Send failures that occur after this check are only logged, not thrown.
Solutions
- Check client.isOpen() before each send, or gate sends on the connection-opened callback
- Re-establish the connection (call connect()) when the close/error listener fires before sending again
- Wrap sends in a catch for WsException and reconnect-and-retry the send
- Review server-side idle/close timeouts and send keep-alive/ping frames
Example fix
// before
client.send("hello"); // may throw if closed
// after
if (!client.isOpen()) { client.connect(url); awaitOpen(client); }
try { client.send("hello"); } catch (WsException e) { reconnectAndResend(client, "hello"); } Defensive patterns
Strategy: validation
Validate before calling
if (!client.isOpen()) { client.connect(url); awaitOpen(client); } Type guard
boolean canSend = client != null && client.isOpen();
Try / catch
try { client.send(frame); } catch (WsException e) { if (e.getType() == WsException.Type.CONNECTION_CLOSED) { reconnect(client); client.send(frame); } } Prevention
- Gate sends on the connection-open callback, not immediately after connect()
- Handle close/error listeners by triggering reconnect before further sends
- Add keep-alive/ping to detect dead connections early
- Avoid sending concurrently with close()
When it happens
Trigger: Calling send(String), send(byte[]) or send(WsFrame) after the WebSocket was closed, before connect() completed, or after the server dropped the connection.
Common situations: Sending immediately after connect() without waiting for the open event; server-side timeout/close mid-session; forgetting to reconnect after an error listener fired; race between close and send in concurrent code.
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
- browser did not return a browserContextId
- CDP connection failed readiness check
- CDP error
- CDP timeout for
- client credentials auth request failed: " + e.getMessage()
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/cf35a3f9f452b733.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/http/WsClient.java:271
}
public String getNegotiatedSubprotocol() {
return negotiatedSubprotocol;
}
// Sending frames
public void send(String text) {
send(WsFrame.text(text));
}
public void send(byte[] binary) {
send(WsFrame.binary(binary));
}
public void send(WsFrame frame) {
if (!isOpen()) {
throw new WsException(WsException.Type.CONNECTION_CLOSED, "websocket is not open");
}
WebSocketFrame wsFrame = toNettyFrame(frame);
channel.writeAndFlush(wsFrame).addListener((ChannelFutureListener) future -> {
if (!future.isSuccess()) {
logger.error("send failed: {}", future.cause().getMessage());
}
});
}
public CompletableFuture<Void> sendAsync(WsFrame frame) {
if (!isOpen()) {
return CompletableFuture.failedFuture(
new WsException(WsException.Type.CONNECTION_CLOSED, "websocket is not open"));
}
CompletableFuture<Void> future = new CompletableFuture<>();
WebSocketFrame wsFrame = toNettyFrame(frame);
channel.writeAndFlush(wsFrame).addListener((ChannelFutureListener) channelFuture -> {
if (channelFuture.isSuccess()) {View on GitHub (pinned to a22eb90246)