nats-io/nats-server · error

invalid websocket connection

Error message

invalid websocket connection

What it means

Set during the leaf-node WebSocket client handshake when the HTTP response is not a valid 101 Switching Protocols upgrade: wrong status code, missing Upgrade/Connection headers, or a Sec-Websocket-Accept key that does not match wsAcceptKey(wsKey). It means the remote endpoint did not complete the WebSocket handshake correctly.

Source

Thrown at server/leafnode.go:3677

	if noMasking {
		req.Header.Add(wsNoMaskingHeader, wsNoMaskingValue)
	}
	c.nc.SetDeadline(time.Now().Add(infoTimeout))
	if err := req.Write(c.nc); err != nil {
		return nil, WriteError, err
	}

	var resp *http.Response

	br := bufio.NewReaderSize(c.nc, MAX_CONTROL_LINE_SIZE)
	resp, err = http.ReadResponse(br, req)
	if err == nil &&
		(resp.StatusCode != 101 ||
			!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
			!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
			resp.Header.Get("Sec-Websocket-Accept") != wsAcceptKey(wsKey)) {

		err = fmt.Errorf("invalid websocket connection")
	}
	// Check compression extension...
	if err == nil && c.ws.compress {
		// Check that not only permessage-deflate extension is present, but that
		// we also have server and client no context take over.
		srvCompress, noCtxTakeover := wsPMCExtensionSupport(resp.Header, false)

		// If server does not support compression, then simply disable it in our side.
		if !srvCompress {
			c.ws.compress = false
		} else if !noCtxTakeover {
			err = fmt.Errorf("compression negotiation error")
		}
	}
	// Same for no masking...
	if err == nil && noMasking {
		// Check if server accepts no masking
		if resp.Header.Get(wsNoMaskingHeader) != wsNoMaskingValue {

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Confirm the leaf node URL actually points to a WebSocket-enabled leaf node port
  2. Check for intermediaries (proxies, load balancers) that strip Upgrade headers
  3. Verify the server supports WebSockets on that port and shares the same accept-key logic
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at server/leafnode.go:3677 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/786297b9cf0850ea. Report an issue: GitHub.