Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Inbound and outbound packet-size checks differ, so a QoS 1 message just under MaxUserPayloadBytes is accepted but never delivered (blocks a persistent subscriber)

Open
#313 7 comments 0 reactions 0 assignees View on GitHub

@abhiramaab is already working on this.

Since Oct 2, 2026.

  • #314 by @abhiramaab — closed without merging

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
55/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Active
Tech stack
java

Research direction

Start with ChannelAttrs.setMaxPayload and the inbound MqttDecoder(maxUserPayloadSize), then compare that limit with MQTT3MessageSizer and MQTTPacketFilter on outbound delivery. Inspect MQTTSessionHandler.writeConfirmableSubMessage and the related resend behavior; done means accepted messages are deliverable or are dropped and confirmed without blocking the subscriber's in-flight window.

Written by the indexing model from the issue text.

Description

Version: 4.0.0-incubating (standalone, default DevOnlySettingProvider)

Summary

MaxUserPayloadBytes (stored as TenantSettings.maxPacketSize) is enforced differently in the two directions:

  • Inbound (publisher → broker): the replacement MqttDecoder(maxPacketSize) compares the fixed header's remaining length R against the limit. A packet is accepted while R ≤ L.
  • Outbound (broker → subscriber): MQTTPacketFilter drops a packet whose full encoded size 1 + varint(R) + R is over the limit (MQTT3MessageSizer), reporting OversizePacketDropped and never completing the write.

So a QoS 1 PUBLISH whose remaining length is within 3 bytes of the limit (R = L−2 … L for L = 16384) is accepted and PUBACKed from the publisher, stored in the subscriber's inbox, and then dropped on every delivery attempt. writeConfirmableSubMessage keeps it in the unconfirmed set and resend() retries it until MaxResendTimes. With a large MaxResendTimes, a persistent subscriber's in-flight window fills with undeliverable messages and no further QoS 1 is delivered to it until the session is reset.

For MQTT 5 subscribers this cannot be worked around from the client: MQTT5ConnectHandler rejects a client Maximum Packet Size above maxPacketSize (CONNACK 0x83), and MQTTPacketFilter uses min(clientMax, settings.maxPacketSize).

Reproduction

  1. Start the broker with -DMaxUserPayloadBytes=16384 (and a large -DMaxResendTimes).
  2. Subscriber: MQTT 3.1.1, cleanSession=false, QoS 1, manual acks.
  3. Publisher (same tenant): QoS 1 publishes with remaining length 16382, 16383, 16384 (payload = R − 2 − topicLength − 2), followed by a small normal message.
  4. The publisher gets PUBACKs for all of them; the subscriber receives none (not even the normal message once ~8 undeliverable messages occupy the window).

Expected

Both directions use the same measure, e.g. the inbound decoder limit is maxPacketSize − 5 (the maximum fixed-header overhead), or the outbound filter compares the remaining length, so anything accepted inbound is deliverable to a subscriber with the same limit. Independently, MQTTSessionHandler.writeConfirmableSubMessage already drops-and-confirms a confirmable message for NoSubPermission, Duplicated and NoLocal; doing the same when the encoded size exceeds the subscriber's limit (report a drop event, confirm(packetId, false)) would stop an undeliverable message from occupying the in-flight window until MaxResendTimes.

Checked against main (2026-10-02, 1394131): still present. #279 ("Fix oversized packet mem leakage", merged 2026-08-31, not yet released) makes MQTTPacketFilter release the buffer and complete the write promise, but the message stays unconfirmed in the session, and the inbound MqttDecoder(maxUserPayloadSize) in ChannelAttrs.setMaxPayload still compares the remaining length.

Related: apache/bifromq#258 (resend cancelled by PUBACK), apache/bifromq#289 (resend chain stops after a MaxRetried drop), #279 (oversized-packet buffer leak).

Dominant language
Java
Stars
805
Forks
99
Avg merge
12h 35m
Merged PRs (30d)
3

Getting set up

  • Ships a Dockerfile or Docker Compose file
  • No pull request template
  • No contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from apache/bifromq

All issues in apache/bifromq

Similar issues

More Java issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.