{"record":{"id":"579727811c5048c4","repo":"nats-io/nats-server","slug":"invalid-publish-message-variable-header-exceeds-r","errorCode":null,"errorMessage":"invalid publish message, variable header exceeds remaining length","messagePattern":"invalid publish message, variable header exceeds remaining length","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/mqtt.go","lineNumber":248,"sourceCode":"\terrMQTTUserMixWithUsersNKeys      = errors.New(\"mqtt authentication username not compatible with presence of users/nkeys\")\n\terrMQTTTokenMixWIthUsersNKeys     = errors.New(\"mqtt authentication token not compatible with presence of users/nkeys\")\n\terrMQTTAckWaitMustBePositive      = errors.New(\"ack wait must be a positive value\")\n\terrMQTTJSAPITimeoutMustBePositive = errors.New(\"JS API timeout must be a positive value\")\n\terrMQTTStandaloneNeedsJetStream   = errors.New(\"mqtt requires JetStream to be enabled if running in standalone mode\")\n\terrMQTTConnFlagReserved           = errors.New(\"connect flags reserved bit not set to 0\")\n\terrMQTTWillAndRetainFlag          = errors.New(\"if Will flag is set to 0, Will Retain flag must be 0 too\")\n\terrMQTTPasswordFlagAndNoUser      = errors.New(\"password flag set but username flag is not\")\n\terrMQTTCIDEmptyNeedsCleanFlag     = errors.New(\"when client ID is empty, clean session flag must be set to 1\")\n\terrMQTTEmptyWillTopic             = errors.New(\"empty Will topic not allowed\")\n\terrMQTTEmptyUsername              = errors.New(\"empty user name not allowed\")\n\terrMQTTTopicIsEmpty               = errors.New(\"topic cannot be empty\")\n\terrMQTTPacketIdentifierIsZero     = errors.New(\"packet identifier cannot be 0\")\n\terrMQTTUnsupportedCharacters      = errors.New(\"character not supported for MQTT topics\")\n\terrMQTTInvalidSession             = errors.New(\"invalid MQTT session\")\n\terrMQTTInvalidRetainFlags         = errors.New(\"invalid retained message flags\")\n\terrMQTTInvalidRetainedMessage     = errors.New(\"invalid retained message\")\n\terrMQTTSessionCollision           = errors.New(\"stored session does not match client ID\")\n\terrMQTTInvalidPublishLength       = errors.New(\"invalid publish message, variable header exceeds remaining length\")\n\terrMQTTAckPipelineStopped         = errors.New(\"QoS1 PUBACK pipeline has shut down while admitting a message, \" +\n\t\t\"abandoning the wait for its JetStream ack; failing the connection, \" +\n\t\t\"the client will re-send unacknowledged PUBLISH packets on reconnect\")\n)\n\ntype srvMQTT struct {\n\tlistener     net.Listener\n\tlistenerErr  error\n\tauthOverride bool\n\tsessmgr      mqttSessionManager\n}\n\ntype mqttSessionManager struct {\n\tmu       sync.RWMutex\n\tsessions map[string]*mqttAccountSessionManager // key is account name\n}\n\ntype mqttAccountSessionManager struct {","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/mqtt.go#L230-L266","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// client-side: validate PUBLISH remaining length before sending\nfunction publishRemainingLength(topic, qos, payload) {\n  const headerLen = 2 + Buffer.byteLength(topic) + (qos > 0 ? 2 : 0) + payload.length;\n  if (headerLen < 2 + Buffer.byteLength(topic) + (qos > 0 ? 2 : 0)) throw new Error('underflow');\n  return headerLen;\n}","typeGuard":null,"tryCatchPattern":"// broker rejects with connack/disconnect; log the raw packet for diagnosis\nconn.on('error', (err) => {\n  if (/variable header exceeds remaining length/.test(err.message)) {\n    logCorruptPacket(lastOutboundPacket); // inspect remaining-length encoding\n  }\n});","preventionTips":["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"],"tags":["mqtt","protocol","packet-parsing","nats-server"],"backgroundTag":"malformed-packet","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}