nats-io/nats-server · error

${message} (dynamic websocket protocol error, sent to client

Error message

${message} (dynamic websocket protocol error, sent to client as close status 1002)

What it means

wsHandleProtocolError sends a WebSocket close frame with status 1002 (protocol error) containing the message to the client, then returns the message as a Go error so the server can close the connection. The message is labeled as a dynamic websocket protocol error surfaced to the client as close status 1002. It indicates the client (or server) violated the WebSocket protocol.

Source

Thrown at server/websocket.go:776

		// status in the close message. So using this one instead.
		status = wsCloseStatusGoingAway
	default:
		status = wsCloseStatusInternalSrvError
	}
	body := wsCreateCloseMessage(status, reason.String())
	c.wsEnqueueControlMessageLocked(wsCloseMessage, body)
	nbPoolPut(body) // wsEnqueueControlMessageLocked has taken a copy.
}

// Create and then enqueue a close message with a protocol error and the
// given message. This is invoked when parsing websocket frames.
//
// Lock MUST NOT be held on entry.
func (c *client) wsHandleProtocolError(message string) error {
	buf := wsCreateCloseMessage(wsCloseStatusProtocolError, message)
	c.wsEnqueueControlMessage(wsCloseMessage, buf)
	nbPoolPut(buf) // wsEnqueueControlMessage has taken a copy.
	return errors.New(message)
}

func wsIsValidCloseStatus(code int) bool {
	switch code {
	case wsCloseStatusNoStatusReceived, 1004, 1006, wsCloseStatusTLSHandshake:
		return false
	}
	if code < 1000 || code >= 5000 {
		return false
	}
	// 1016-2999 are currently reserved.
	if code >= 1016 && code <= 2999 {
		return false
	}
	return true
}

// Create a close message with the given `status` and `body`.

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Fix the client to send spec-compliant WebSocket frames (correct opcodes, masking, control-frame sizes)
  2. Inspect the error message text — it is passed through as the close reason and names the exact protocol violation
  3. Check for intermediaries (proxies, load balancers) that rewrite or buffer websocket frames and bypass them
  4. Update client and server libraries to compatible versions

Example fix

// before: client sends a text frame where a binary frame is expected
ws.send(JSON.stringify(payload));
// after
ws.send(new TextEncoder().encode(JSON.stringify(payload))); // binary per NATS websocket protocol
Defensive patterns

Strategy: try-catch

Try / catch

// browser client
ws.onclose = (ev) => {
  if (ev.code === 1002) {
    console.error('websocket protocol error, server reason:', ev.reason);
    // log client frame state and reconnect only after fixing the client frames
  }
};

Prevention

When it happens

Trigger: Any code path in the server's websocket client handling that detects a protocol violation and calls c.wsHandleProtocolError(msg), e.g. malformed frames, invalid opcodes, bad control frames, or unsupported data during a websocket session.

Common situations: A non-NATS websocket client (browser dev tools, proxies, custom clients) sending malformed frames; intermediaries/proxies mangling websocket traffic; protocol negotiation bugs after server upgrades.

Related errors


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