{"record":{"id":"4b0391feac9121a4","repo":"nats-io/nats-server","slug":"invalid-remaining-length-d-for-packet-type-x","errorCode":null,"errorMessage":"invalid remaining length %d for packet type %x","messagePattern":"invalid remaining length (.+?) for packet type %x","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/mqtt.go","lineNumber":1030,"sourceCode":"\t\treturn fmt.Errorf(\"invalid fixed header flags %x for packet type %x\", flags, packetType)\n\t}\n\treturn nil\n}\n\nfunc mqttCheckRemainingLength(packetType byte, pl int) error {\n\tvar expected int\n\tswitch packetType {\n\tcase mqttPacketConnect, mqttPacketPub, mqttPacketSub, mqttPacketUnsub:\n\t\treturn nil\n\tcase mqttPacketPubAck, mqttPacketPubRec, mqttPacketPubRel, mqttPacketPubComp:\n\t\texpected = 2\n\tcase mqttPacketPing, mqttPacketDisconnect:\n\t\texpected = 0\n\tdefault:\n\t\treturn nil\n\t}\n\tif pl != expected {\n\t\treturn fmt.Errorf(\"invalid remaining length %d for packet type %x\", pl, packetType)\n\t}\n\treturn nil\n}\n\nfunc (c *client) mqttTraceMsg(msg []byte) {\n\tmaxTrace := c.srv.getOpts().MaxTracedMsgLen\n\tif maxTrace > 0 && len(msg) > maxTrace {\n\t\tc.Tracef(\"<<- MSG_PAYLOAD: [\\\"%s...\\\"]\", msg[:maxTrace])\n\t} else {\n\t\tc.Tracef(\"<<- MSG_PAYLOAD: [%q]\", msg)\n\t}\n}\n\n// The MQTT client connection has been closed, or the DISCONNECT packet was received.\n// For a \"clean\" session, we will delete the session, otherwise, simply removing\n// the binding. We will also send the \"will\" message if applicable.\n//\n// Runs from the client's readLoop.","sourceCodeStart":1012,"sourceCodeEnd":1048,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/mqtt.go#L1012-L1048","documentation":"For certain MQTT control packets (PINGREQ, PINGRESP, DISCONNECT, etc.), the spec mandates a remaining length of exactly 0 bytes. mqttCheckRemainingLength enforces this after the varint remaining-length is decoded and returns this error when pl != expected. It guards against clients sending payloads on packets that must be empty.","triggerScenarios":"A client sends PINGREQ or DISCONNECT (or another fixed-length type) with a nonzero remaining-length varint. Triggered in mqttCheckRemainingLength when pl != expected.","commonSituations":"Client libraries appending trailing bytes or session state to DISCONNECT; corruption inserting bytes; hand-rolled encoders computing length incorrectly; fuzz tests.","solutions":["Fix the client encoder so PINGREQ/DISCONNECT have zero-length remaining length.","Enable MQTT trace to inspect the raw packet and remaining-length bytes.","Check for buggy middleware that appends padding to frames.","Reconnect with a conformant client; the server correctly rejects the malformed packet per spec."],"exampleFix":"// before\nsendPacket(DISCONNECT, payload=extraBytes)\n// after\nsendPacket(DISCONNECT, remainingLength=0)","handlingStrategy":"validation","validationCode":"// Enforce zero remaining length for PINGREQ/DISCONNECT etc.\nconst ZERO_LEN_TYPES = new Set([12, 13, 14]); // PINGREQ, PINGRESP, DISCONNECT\nif (ZERO_LEN_TYPES.has(type) && remainingLength !== 0) throw new Error('payload not allowed on type ' + type);","typeGuard":"function requiresZeroRemainingLength(packetType) { return [12, 13, 14].includes(packetType); }","tryCatchPattern":null,"preventionTips":["Send DISCONNECT/PINGREQ with empty bodies","Don't append trailing/padding bytes to control packets","Compute remaining length from an empty payload for fixed-length types","Fuzz-test your encoder against a spec-conformant broker"],"tags":["mqtt","protocol-violation","malformed-packet"],"backgroundTag":"malformed-mqtt-header","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}