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

  1. 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)
  2. Update the client library to a recent version known to serialize PUBLISH correctly
  3. Inspect any intermediary proxies/load balancers for buffer truncation or protocol rewriting
  4. 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

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


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