nats-io/nats-server · error

wrong %ssubscribe reserved flags: %x

Error message

wrong %ssubscribe reserved flags: %x

What it means

The SUBSCRIBE and UNSUBSCRIBE packets must have fixed reserved flag bits set to a fixed value per the MQTT spec ([MQTT-3.8.1-1], [MQTT-3.10.1-1]); the server parses the first header byte and rejects any other flag combination. The %s placeholder reads "" (subscribe) or "un" (unsubscribe).

Source

Thrown at server/mqtt.go:5301

//
//////////////////////////////////////////////////////////////////////////////

func (c *client) mqttParseSubs(r *mqttReader, b byte, pl int) (uint16, []*mqttFilter, error) {
	return c.mqttParseSubsOrUnsubs(r, b, pl, true)
}

func (c *client) mqttParseSubsOrUnsubs(r *mqttReader, b byte, pl int, sub bool) (uint16, []*mqttFilter, error) {
	var expectedFlag byte
	var action string
	if sub {
		expectedFlag = mqttSubscribeFlags
	} else {
		expectedFlag = mqttUnsubscribeFlags
		action = "un"
	}
	// Spec [MQTT-3.8.1-1], [MQTT-3.10.1-1]
	if rf := b & 0xf; rf != expectedFlag {
		return 0, nil, fmt.Errorf("wrong %ssubscribe reserved flags: %x", action, rf)
	}
	pi, err := mqttParsePIPacket(r)
	if err != nil {
		return 0, nil, err
	}
	end := r.pos + (pl - 2)
	var filters []*mqttFilter
	for r.pos < end {
		// Don't make a copy now because, this will happen during conversion
		// or when processing the sub.
		topic, err := r.readBytes("topic filter", false)
		if err != nil {
			return 0, nil, err
		}
		if len(topic) == 0 {
			return 0, nil, errMQTTTopicFilterCannotBeEmpty
		}
		// Spec [MQTT-3.8.3-1], [MQTT-3.10.3-1]

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Set the SUBSCRIBE/UNSUBSCRIBE fixed header correctly: byte = packetType<<4 | 0x02
  2. Enable TLS to eliminate in-transit byte corruption and retest
  3. Verify the client library version supports the MQTT version negotiated and upgrade if buggy
  4. Inspect the raw first header byte with a packet capture to confirm which flags the client sends

Example fix

// before (client-side header)
header := byte(subscribeType)<<4 // flags = 0000, wrong
// after
header := byte(subscribeType)<<4 | 0x02 // reserved flags per spec
Defensive patterns

Strategy: validation

Validate before calling

func subscribeHeader(packetType byte) byte { return packetType<<4 | 0x02 } // required reserved flags for SUB/UNSUB

Type guard

func hasValidSubscribeFlags(b byte) bool { return b&0x0f == 0x02 }

Try / catch

if err := subscribe(topic); err != nil && strings.Contains(err.Error(), "reserved flags") { log.Printf("fixing header flags: %v", err); rebuildPacketWithFlags0x02() }

Prevention

When it happens

Trigger: A client sends a SUBSCRIBE (expected fixed flags 0x2) or UNSUBSCRIBE (expected 0x2) whose low 4 header bits differ, e.g. 0x0, 0x3, or QoS bits erroneously set.

Common situations: Hand-rolled packet encoders forgetting the required 0010 bits, protocol fuzzers, corrupted header bytes on a non-TLS link, or a client library using an incompatible MQTT version framing.

Related errors


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