WebSocket persistent 1002 protocol error

,

Hi Community,

I’ve been attempting to load test a WebSocket endpoint that uses the STOMP protocol over WSS. Despite exhaustive testing and replicating a known working Python client, I am consistently hitting a websocket: close 1002 (protocol error) immediately after the WebSocket connection opens.
This issue seems to be a deep-rooted incompatibility between the underlying GO WebSocket client used by k6 and the API Gateway.

Background and working client
I have a working Python client using the websocket-client library that successfully connects, sends the STOMP CONNECT frame, and receives the CONNECTED response.

# This builds the STOMP CONNECT frame
connect_frame = f"""CONNECT
accept-version:1.2
X-Vault-Session:{session_key}
X-Vault-Endpoint:{endpoint_url}

\x00
"""

ws = None
recv_thread = None
try:
  print(f"Connecting to {url}...")
  ws = websocket.create_connection(url, sslopt={"cert_reqs": ssl.CERT_NONE})

  print("WebSocket tunnel OPEN.")
  print("Sending STOMP CONNECT frame...")
  print("RAW STOMP CONNECT BYTES:\n", connect_frame.encode())
  ws.send(connect_frame)

  print("Waiting for CONNECTED response...")
  response = ws.recv()

  print("\n--- Server Response ---")
  print(response)
  print("-----------------------\n")

  if "CONNECTED" in response:
    print("✅ SUCCESS! You are connected and authenticated.")

  else:
    print("❌ STOMP CONNECT FAILED. Server did not send 'CONNECTED'.")

Summary of Attempts (all failed with 1002 error)

I tested 2 methods: The 3rd-party extension (k6/x/stomp) and the native k6/ws module, meticulously replicating the successful Python client’s behavior.

  • Using k6/x/stomp custom extension
import stomp from "k6/x/stomp";

export function connectWebsocket(vaultEndpoint, vaultSession, xChatEndpoint, vcrmVersion = "main") {
  const headers = {
    "X-Vault-Endpoint": vaultEndpoint,
    "X-Vault-Session": vaultSession,
  };
  const path = `/${vcrmVersion}/api/xxx/xxx`;
  const connAddr = `${xChatEndpoint}:443`;

  return stomp.connect({
    protocol: "wss",
    host: xChatEndpoint,
    addr: connAddr,
    path: path,
    verbose: true,
  });
}

Below is from the verbose log

INFO[0000] 2025/12/03 16:41:38 WRITE: CONNECT
INFO[0000] host:xxx.com
INFO[0000] heart-beat:60000,60000
INFO[0000] accept-version:1.0,1.1,1.2
INFO[0000]
INFO[0000]
INFO[0000] 2025/12/03 16:41:38 READ-ERR: websocket: close 1002 (protocol error): Protocol error
ERRO[0000] GoError: websocket: close 1002 (protocol error): Protocol error
running at reflect.methodValueCall (native)

  • Using native k6/ws by manually implementing STOMP protocol
// EXACT Python frame, sent as TEXT
function buildStompConnectFrame() {
  return (
    "CONNECT\n" +
    "accept-version:1.2\n" +
    `X-Vault-Session:${vaultSession}\n` +
    `X-Vault-Endpoint:${vaultEndpoint}\n` +
    "\n" +
    "\x00\n"
  );
}

export default function () {
  ws.connect(
    url,
    {
      timeout: "20s",
      insecureSkipTLSVerify: true,
    },
    function (socket) {
      socket.on("open", () => {
        console.log("OPEN → sending STOMP CONNECT as TEXT");
        socket.send(buildStompConnectFrame()); // TEXT FRAME
      });

      socket.on("error", (e) => console.log("ERROR:", e));
      socket.on("close", () => console.log("CLOSED"));
    }
  );
}

INFO[0000] OPEN → sending STOMP CONNECT as TEXT source=console
INFO[0000] ERROR: websocket: close 1002 (protocol error): Protocol error source=console
INFO[0000] CLOSED source=console

Has anyone else encountered this level of incompatibility with specific API gateways? Are there any hidden k6 environment variables or global options that can influence the low-level handshake format?
From ChatGPT, it says the main issue is that k6 does not support WebSocket RFC 7692 permessage-deflate