nats-io/nats-server · error
with QoS=%v, packet identifier cannot be 0
Error message
with QoS=%v, packet identifier cannot be 0
What it means
A PUBLISH packet with QoS 1 or 2 must carry a non-zero packet identifier per the MQTT spec ([MQTT-2.3.1-1]); identifier 0 is reserved for QoS 0 packets. The server rejects the packet during parsing because a zero pi would collide with QoS0 semantics and break acknowledgement tracking.
Source
Thrown at server/mqtt.go:4302
if changed := c.selectMappedSubject(); changed {
// We need to keep track of the NATS subject/mapped in the `pp` structure.
pp.subject = c.pa.subject
pp.mapped = c.pa.mapped
// We also now need to map the original MQTT topic to the new topic
// based on the new subject.
pp.topic = natsSubjectToMQTTTopic(pp.subject)
}
// Reset those now.
c.pa.subject, c.pa.mapped = nil, nil
}
if qos > 0 {
pp.pi, err = r.readUint16("packet identifier")
if err != nil {
return err
}
if pp.pi == 0 {
return fmt.Errorf("with QoS=%v, packet identifier cannot be 0", qos)
}
} else {
pp.pi = 0
}
// The message payload will be the total packet length minus
// what we have consumed for the variable header
payloadSize := pl - (r.pos - start)
if payloadSize < 0 {
return fmt.Errorf("invalid remaining length %d for PUBLISH packet", pl)
}
pp.sz = payloadSize
if pp.sz > 0 {
start = r.pos
r.pos += pp.sz
pp.msg = r.buf[start:r.pos]
} else if pp.sz == 0 {
pp.msg = nilView on GitHub (pinned to 3a66a489d2)
Solutions
- Fix the client to generate a non-zero packet identifier (e.g. an incrementing counter starting at 1) for QoS>0 PUBLISH packets
- Set QoS to 0 if no acknowledgement/delivery guarantee is needed, in which case pi may be 0 or omitted
- Capture the client's outbound traffic and verify the pi bytes (positions right after the topic length/topic) are non-zero
- Upgrade or replace the offending client library to a spec-conformant version
Example fix
// before (client-side encoding)
pi := 0
writeUint16(pi)
// after
if qos > 0 && pi == 0 { pi = nextPacketID() }
writeUint16(pi) Defensive patterns
Strategy: validation
Validate before calling
func validPublish(qos byte, pi uint16) error {
if qos > 0 && pi == 0 { return errors.New("packet identifier must be non-zero for QoS>0") }
return nil
} Type guard
func hasValidPacketID(qos byte, pi uint16) bool { return qos == 0 || pi != 0 } Prevention
- Use an incrementing packet-ID counter starting at 1, wrapping at 65535 to 1
- Reserve pi=0 exclusively for QoS0 publishes
- Fuzz-test your encoder against a spec-conformant broker before production
- Prefer a maintained MQTT client library over hand-built packets
When it happens
Trigger: A client sends a PUBLISH packet whose fixed-header QoS bits are 1 or 2 but whose variable-header packet identifier field encodes 0 (two zero bytes).
Common situations: Buggy or hand-rolled MQTT clients constructing packets manually, fuzzers, corrupted frames from a faulty broker/bridge, or off-by-one encoding where the pi field is omitted or zero-filled.
Related errors
- packet identifier cannot be 0
- the first packet should be a CONNECT (%v), got %v
- received unknown packet type %d
- invalid fixed header flags %x for packet type %x
- invalid remaining length %d for packet type %x
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/55ea3128231af1c0.
Report an issue: GitHub.