TooTallNate/Java-WebSocket · error · InvalidHandshakeException

missing Sec-WebSocket-Key

Error message

missing Sec-WebSocket-Key

What it means

When acting as a server, Draft_6455 must include the Sec-WebSocket-Accept header in the 101 response, computed from the client's Sec-WebSocket-Key per RFC 6455. If the incoming client handshake lacks a Sec-WebSocket-Key header (empty or missing), postProcessHandshakeResponseAsServer throws this InvalidHandshakeException and the upgrade fails.

Solutions

  1. Make the connecting client send Sec-WebSocket-Key (any real RFC 6455 client library does)
  2. Exclude the WebSocket port from health-check probes or configure a TCP-level check instead
  3. If writing a raw client, include "Sec-WebSocket-Key: <base64 16-byte value>" in the request
  4. Log the offending client handshake to identify the non-conformant source

Example fix

// before (raw client request)
output.write("GET /ws HTTP/1.1\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n");
// after
output.write("GET /ws HTTP/1.1\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n");
Defensive patterns

Strategy: try-catch

Validate before calling

// before accepting an upgrade as server, verify the client sent the key
String key = request.getFieldValue("Sec-WebSocket-Key");
if (key == null || key.isEmpty()) {
  // reject/hand off: not a valid RFC 6455 client
  return AbortHandshake.forInvalidRequestCode(400);
}

Try / catch

public ServerHandshakeBuilder onWebsocketHandshakeReceivedAsServer(WebSocket conn, Draft draft, ClientHandshake request) throws InvalidHandshakeException {
  if (request.getFieldValue("Sec-WebSocket-Key").isEmpty())
    throw new InvalidHandshakeException("not a WebSocket client");
  return super.onWebsocketHandshakeReceivedAsServer(conn, draft, request);
}

Prevention

When it happens

Trigger: A client connects without sending the Sec-WebSocket-Key header — e.g. a hand-rolled HTTP client, a health check or plain HTTP probe hitting the WS port, or a very old/non-conformant WebSocket client.

Common situations: Load balancer health checks port-scanning the WebSocket server, custom client code forgetting the required handshake headers, tools sending plain HTTP requests to the WS endpoint.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of TooTallNate/Java-WebSocket@afeacbf8c0 (2026-09-09). Data as JSON: /api/errors/41dc4551b1e3631a. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/org/java_websocket/drafts/Draft_6455.java:439

        }
        requestedProtocols.append(knownProtocol.getProvidedProtocol());
      }
    }
    if (requestedProtocols.length() != 0) {
      request.put(SEC_WEB_SOCKET_PROTOCOL, requestedProtocols.toString());
    }
    return request;
  }

  @Override
  public HandshakeBuilder postProcessHandshakeResponseAsServer(ClientHandshake request,
      ServerHandshakeBuilder response) throws InvalidHandshakeException {
    response.put(UPGRADE, "websocket");
    response.put(CONNECTION,
        request.getFieldValue(CONNECTION)); // to respond to a Connection keep alives
    String seckey = request.getFieldValue(SEC_WEB_SOCKET_KEY);
    if (seckey == null || "".equals(seckey)) {
      throw new InvalidHandshakeException("missing Sec-WebSocket-Key");
    }
    response.put(SEC_WEB_SOCKET_ACCEPT, generateFinalKey(seckey));
    if (getExtension().getProvidedExtensionAsServer().length() != 0) {
      response.put(SEC_WEB_SOCKET_EXTENSIONS, getExtension().getProvidedExtensionAsServer());
    }
    if (getProtocol() != null && getProtocol().getProvidedProtocol().length() != 0) {
      response.put(SEC_WEB_SOCKET_PROTOCOL, getProtocol().getProvidedProtocol());
    }
    response.setHttpStatusMessage("Web Socket Protocol Handshake");
    response.put("Server", "TooTallNate Java-WebSocket");
    response.put("Date", getServerTime());
    return response;
  }

  @Override
  public Draft copyInstance() {
    ArrayList<IExtension> newExtensions = new ArrayList<>();
    for (IExtension knownExtension : getKnownExtensions()) {

View on GitHub (pinned to afeacbf8c0)