nats-io/nats-server · error
invalid publish message, variable header exceeds remaining l
Error message
invalid publish message, variable header exceeds remaining length
What it means
This sentinel error (errMQTTInvalidPublishLength) is returned while parsing an MQTT PUBLISH packet when the variable header (topic name plus optional packet identifier) claims more bytes than remain in the packet's remaining-length field. The decoder at server/mqtt.go:4322 cannot read the header, so the packet is declared malformed and the connection is rejected. It protects the server from under-length or truncated PUBLISH frames.
Source
Thrown at server/mqtt.go:248
errMQTTUserMixWithUsersNKeys = errors.New("mqtt authentication username not compatible with presence of users/nkeys")
errMQTTTokenMixWIthUsersNKeys = errors.New("mqtt authentication token not compatible with presence of users/nkeys")
errMQTTAckWaitMustBePositive = errors.New("ack wait must be a positive value")
errMQTTJSAPITimeoutMustBePositive = errors.New("JS API timeout must be a positive value")
errMQTTStandaloneNeedsJetStream = errors.New("mqtt requires JetStream to be enabled if running in standalone mode")
errMQTTConnFlagReserved = errors.New("connect flags reserved bit not set to 0")
errMQTTWillAndRetainFlag = errors.New("if Will flag is set to 0, Will Retain flag must be 0 too")
errMQTTPasswordFlagAndNoUser = errors.New("password flag set but username flag is not")
errMQTTCIDEmptyNeedsCleanFlag = errors.New("when client ID is empty, clean session flag must be set to 1")
errMQTTEmptyWillTopic = errors.New("empty Will topic not allowed")
errMQTTEmptyUsername = errors.New("empty user name not allowed")
errMQTTTopicIsEmpty = errors.New("topic cannot be empty")
errMQTTPacketIdentifierIsZero = errors.New("packet identifier cannot be 0")
errMQTTUnsupportedCharacters = errors.New("character not supported for MQTT topics")
errMQTTInvalidSession = errors.New("invalid MQTT session")
errMQTTInvalidRetainFlags = errors.New("invalid retained message flags")
errMQTTInvalidRetainedMessage = errors.New("invalid retained message")
errMQTTSessionCollision = errors.New("stored session does not match client ID")
errMQTTInvalidPublishLength = errors.New("invalid publish message, variable header exceeds remaining length")
errMQTTAckPipelineStopped = errors.New("QoS1 PUBACK pipeline has shut down while admitting a message, " +
"abandoning the wait for its JetStream ack; failing the connection, " +
"the client will re-send unacknowledged PUBLISH packets on reconnect")
)
type srvMQTT struct {
listener net.Listener
listenerErr error
authOverride bool
sessmgr mqttSessionManager
}
type mqttSessionManager struct {
mu sync.RWMutex
sessions map[string]*mqttAccountSessionManager // key is account name
}
type mqttAccountSessionManager struct {View on GitHub (pinned to 3a66a489d2)
Solutions
- Fix the client so it correctly computes the PUBLISH remaining-length field (topic length + topic bytes + 2-byte packet id for QoS 1/2 + payload)
- Update the client library to a recent version known to serialize PUBLISH correctly
- Inspect any intermediary proxies/load balancers for buffer truncation or protocol rewriting
- Capture the offending packet (tcpdump/Wireshark) to confirm the packet is malformed before blaming the server
Defensive patterns
Strategy: validation
Validate before calling
// client-side: validate PUBLISH remaining length before sending
function publishRemainingLength(topic, qos, payload) {
const headerLen = 2 + Buffer.byteLength(topic) + (qos > 0 ? 2 : 0) + payload.length;
if (headerLen < 2 + Buffer.byteLength(topic) + (qos > 0 ? 2 : 0)) throw new Error('underflow');
return headerLen;
} Try / catch
// broker rejects with connack/disconnect; log the raw packet for diagnosis
conn.on('error', (err) => {
if (/variable header exceeds remaining length/.test(err.message)) {
logCorruptPacket(lastOutboundPacket); // inspect remaining-length encoding
}
}); Prevention
- Use a maintained, spec-conformant MQTT client library instead of hand-rolled packet encoding
- Unit-test PUBLISH encoding for QoS 0/1/2 including remaining-length varint calculation
- Audit proxies/load balancers between client and broker for packet truncation
When it happens
Trigger: An MQTT client sends a PUBLISH packet whose remaining length is smaller than the topic length prefix (plus 2 bytes for QoS>0 packet identifier), i.e. a truncated or corrupt packet, triggering the 'variable header exceeds remaining length' check.
Common situations: Buggy or hand-rolled MQTT clients computing remaining length incorrectly; packet truncation from a misbehaving proxy/LB with small buffers; firmware bugs on embedded devices; corruption from mixing MQTT versions on one port.
Related errors
- topic filter cannot be empty
- malformed variable int
- received a second CONNECT packet
- connect flags reserved bit not set to 0
- if Will flag is set to 0, Will Retain flag must be 0 too
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/579727811c5048c4.
Report an issue: GitHub.