{"record":{"id":"c9b96c992305ecca","repo":"nats-io/nats-server","slug":"invalid-remaining-length-d-for-publish-packet","errorCode":null,"errorMessage":"invalid remaining length %d for PUBLISH packet","messagePattern":"invalid remaining length (.+?) for PUBLISH packet","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/mqtt.go","lineNumber":4312,"sourceCode":"\t}\n\n\tif qos > 0 {\n\t\tpp.pi, err = r.readUint16(\"packet identifier\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif pp.pi == 0 {\n\t\t\treturn fmt.Errorf(\"with QoS=%v, packet identifier cannot be 0\", qos)\n\t\t}\n\t} else {\n\t\tpp.pi = 0\n\t}\n\n\t// The message payload will be the total packet length minus\n\t// what we have consumed for the variable header\n\tpayloadSize := pl - (r.pos - start)\n\tif payloadSize < 0 {\n\t\treturn fmt.Errorf(\"invalid remaining length %d for PUBLISH packet\", pl)\n\t}\n\tpp.sz = payloadSize\n\tif pp.sz > 0 {\n\t\tstart = r.pos\n\t\tr.pos += pp.sz\n\t\tpp.msg = r.buf[start:r.pos]\n\t} else if pp.sz == 0 {\n\t\tpp.msg = nil\n\t} else {\n\t\treturn errMQTTInvalidPublishLength\n\t}\n\treturn nil\n}\n\nfunc mqttValidateTopic(topic []byte, field string) error {\n\tif !utf8.Valid(topic) {\n\t\treturn fmt.Errorf(\"invalid utf8 for %s %q\", field, topic)\n\t}","sourceCodeStart":4294,"sourceCodeEnd":4330,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/mqtt.go#L4294-L4330","documentation":"The computed PUBLISH payload size (remaining length pl minus the variable header bytes consumed) came out negative, meaning the declared remaining length is smaller than the variable header itself. The server rejects the packet as malformed because the frame is internally inconsistent.","triggerScenarios":"A PUBLISH packet whose remaining-length field is less than the combined size of topic length, topic, and (for QoS>0) packet identifier bytes.","commonSituations":"Truncated or corrupted TCP streams without TLS/checksum protection, hand-written packet encoders computing remaining length incorrectly, fuzz testing, or a buggy client library miscounting header bytes.","solutions":["Fix the encoder to compute remaining length as topic+payload sizes (plus 2 for pi when QoS>0) before writing the header","Enable TLS on the client connection to rule out stream corruption in transit","Verify the client library version for known framing bugs and upgrade","Use a MQTT protocol analyzer (e.g. Wireshark MQTT dissector) on the client connection to inspect the malformed frame"],"exampleFix":"// before\npl := len(topic) + len(payload)\n// after\npl := 2 + len(topic) + len(payload)\nif qos > 0 { pl += 2 }","handlingStrategy":"validation","validationCode":"func publishRemainingLen(topic []byte, payload []byte, qos byte) int {\n  n := 2 + len(topic) + len(payload)\n  if qos > 0 { n += 2 }\n  return n\n}","typeGuard":"func hasPlausibleFrame(pl int, headerLen int) bool { return pl >= headerLen }","tryCatchPattern":"if _, err := conn.Write(pkt); err != nil || brokerClosed { log.Printf(\"malformed PUBLISH rejected: %v\", err); reencodeAndResend() }","preventionTips":["Compute remaining length after the variable header is fully assembled","Unit-test PUBLISH encoding with empty topics and empty payloads","Use TLS to detect/circumvent stream corruption","Validate frames with a MQTT dissector during development"],"tags":["mqtt","malformed-packet","framing","publish"],"backgroundTag":"mqtt-protocol-violation","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}