nats-io/nats-server · error

the first packet should be a CONNECT (%v), got %v

Error message

the first packet should be a CONNECT (%v), got %v

What it means

The NATS MQTT client-read loop requires the very first MQTT packet on a plain connection to be a CONNECT (packet type 1, mqttPacketConnect). This error is raised when the first received packet is some other MQTT packet type. A special-case sibling error (errMQTTNotWebsocketPort) is returned when the payload looks like an HTTP GET, meaning the client dialed the MQTT port with an HTTP/Websocket request.

Source

Thrown at server/mqtt.go:812

		// Read packet type and flags
		if b, err = r.readByte("packet type"); err != nil {
			break
		}

		// Packet type
		pt := b & mqttPacketMask

		// If client was not connected yet, the first packet must be
		// a mqttPacketConnect otherwise we fail the connection.
		if !connected && pt != mqttPacketConnect {
			// If the buffer indicates that it may be a websocket handshake
			// but the client is not websocket, it means that the client
			// connected to the MQTT port instead of the Websocket port.
			if bytes.HasPrefix(buf, []byte("GET ")) && !c.isWebsocket() {
				err = errMQTTNotWebsocketPort
			} else {
				err = fmt.Errorf("the first packet should be a CONNECT (%v), got %v", mqttPacketConnect, pt)
			}
			break
		}
		if err = mqttCheckFixedHeaderFlags(pt, b&mqttPacketFlagMask); err != nil {
			break
		}

		maxLen := int32(jwt.NoLimit)
		if !connected {
			maxLen = atomic.LoadInt32(&c.mpay)
		}
		pl, complete, err = r.readPacketLen(maxLen)
		if err != nil || !complete {
			if err == ErrMaxPayload {
				c.maxPayloadViolation(pl, maxLen)
			}
			break
		}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Configure the client to open the connection with a CONNECT packet before anything else (do not send PINGREQ/SUBSCRIBE first).
  2. Verify the client targets the MQTT port, not the HTTP monitoring port (:8222) or websocket port; use ws:// only on the websocket listener.
  3. Exclude this port from HTTP health checks or point probes at the monitoring port instead.
  4. Check for intermediaries (proxies, load balancers) injecting bytes before the client's first packet.

Example fix

// before: probe sends HTTP to MQTT port
curl http://host:1883/

// after: probe the monitoring port
curl http://host:8222/healthz
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the endpoint is the MQTT port and the client's first action is CONNECT
const isMQTTPort = (url.port === '1883' || url.port === '8883') && !url.protocol.startsWith('http');
if (!isMQTTPort) throw new Error(`Point MQTT client at MQTT port, not ${url}`);

Type guard

function isMQTTURL(u) { try { const url = new URL(u); return ['mqtt:','mqtts:','tcp:','tls:'].includes(url.protocol); } catch { return false; } }

Prevention

When it happens

Trigger: A TCP client connects to the MQTT listener (default :1883) and sends a non-CONNECT packet first (e.g. PINGREQ, SUBSCRIBE, or garbage bytes), or sends an HTTP 'GET ' request to the MQTT port instead of the Websocket port. Detected in mqttCheckFixedHeaderFlags/read loop parsing the first packet type.

Common situations: Pointing an HTTP client, health-check probe, or browser at the MQTT port; a load balancer sending its own probe bytes; a client library misconfigured with the wrong port/scheme (ws:// vs tcp://); port confusion between 1883 (MQTT) and the websocket listener; fuzzing/robustness testing with arbitrary bytes.

Related errors


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