karatelabs/karate · error · IllegalStateException
unexpected FullHttpResponse
Error message
unexpected FullHttpResponse (status=${response.status()}, content=${response.content()}) What it means
WsClientHandler.channelRead0 throws IllegalStateException when a FullHttpResponse arrives after (or outside) the expected handshake processing. In a normal WebSocket lifecycle the handshake response is consumed once; any subsequent full HTTP response on the channel means the pipeline received something it cannot interpret as a WebSocket frame, so the library fails loudly and fails the handshake future.
Solutions
- Check the handshakeFuture failure/cause and inspect the status/content in the message to see what the server returned
- Verify the WebSocket endpoint URL, path and auth headers (Authorization, cookies) are correct
- Ensure any proxy in front of the server supports and forwards the HTTP Upgrade for WebSocket
- Capture a wire-level trace (or server access log) of the handshake request/response to confirm a 101 Switching Protocols
Example fix
// before
client.connect("wss://host/api/ws"); // /api/ws returns 404 JSON page
// after
client.connect("wss://host/ws"); // correct endpoint returns 101 Defensive patterns
Strategy: try-catch
Validate before calling
// pre-flight: confirm the endpoint performs a WS upgrade
HttpRequest req = HttpRequest.newBuilder(URI.create(wsUrl.replaceFirst("^ws", "http"))).GET().build();
// expect non-error response and Upgrade headers; a 404/401 page predicts this IllegalStateException Try / catch
try { client.connect(url).get(10, TimeUnit.SECONDS); } catch (ExecutionException e) { if (e.getCause() instanceof IllegalStateException && e.getCause().getMessage().startsWith("unexpected FullHttpResponse")) { log.error("server rejected upgrade: {}", e.getCause().getMessage()); } } Prevention
- Confirm the ws URL path exists and the handshake returns 101
- Attach auth headers/cookies the server requires for the upgrade
- Ensure proxies/load balancers forward Upgrade and Connection headers
- Read the status/content in the error message to diagnose server rejection
When it happens
Trigger: Server responds with an HTTP error page (e.g. 401/403/404/500) instead of completing the 101 upgrade; duplicate/malformed handshake handling; an HTTP response arriving on an already-established WebSocket channel.
Common situations: Auth gateway returning 302/401 HTML for wss:// requests; reverse proxy intercepting the upgrade; wrong path on the server; server version returning an error after partial handshake.
Related errors
- CONNECT_FAILED
- failed to generate Netty SSL context
- failed to create SSL context from files
- CDP connection failed readiness check
- CDP timeout for
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/bc87b0402ce0bb1f.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/http/WsClientHandler.java:88
client.handleDisconnect();
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
if (!handshaker.isHandshakeComplete()) {
try {
handshaker.finishHandshake(ctx.channel(), (FullHttpResponse) msg);
logger.debug("websocket handshake complete: {}", client.getUri());
handshakeFuture.setSuccess();
} catch (WebSocketHandshakeException e) {
logger.error("websocket handshake failed: {}", e.getMessage());
handshakeFuture.setFailure(e);
}
return;
}
if (msg instanceof FullHttpResponse response) {
throw new IllegalStateException(
"unexpected FullHttpResponse (status=" + response.status() +
", content=" + response.content().toString(StandardCharsets.UTF_8) + ")");
}
WebSocketFrame frame = (WebSocketFrame) msg;
if (frame instanceof TextWebSocketFrame textFrame) {
onTextFrame(ctx, textFrame);
} else if (frame instanceof BinaryWebSocketFrame binaryFrame) {
onBinaryFrame(ctx, binaryFrame);
} else if (frame instanceof PingWebSocketFrame pingFrame) {
onPingFrame(ctx, pingFrame);
} else if (frame instanceof PongWebSocketFrame pongFrame) {
onPongFrame(ctx, pongFrame);
} else if (frame instanceof CloseWebSocketFrame closeFrame) {
onCloseFrame(ctx, closeFrame);
}
}View on GitHub (pinned to a22eb90246)