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

  1. Check client.isOpen() before each send, or gate sends on the connection-opened callback
  2. Re-establish the connection (call connect()) when the close/error listener fires before sending again
  3. Wrap sends in a catch for WsException and reconnect-and-retry the send
  4. 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

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


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)