How to Fix MQTT QoS Errors
In this tutorial, you'll learn about How to Fix MQTT QoS Errors. We cover key concepts, practical examples, and best practices.
The Problem
Your MQTT messages are not reliably delivered despite setting QoS 2, or you receive the same message multiple times. The broker log shows Too many PUBREL packets or MQTT protocol violation. QoS configuration errors undermine the reliability of your IoT messaging.
Quick Fix
Fix 1: Choosing the Wrong QoS Level
WRONG — using QoS 0 for critical commands:
client.publish("actuator/valve", "open", qos=0)
# (message may be lost if the connection drops)
RIGHT — match QoS to use case:
# QoS 0 (At most once) — for non-critical sensor data
client.publish("sensors/temp", "23.5", qos=0)
# (fire and forget — fastest, no guarantee)
# QoS 1 (At least once) — for most commands
client.publish("actuator/valve", "open", qos=1)
# (guaranteed delivery, may duplicate)
# QoS 2 (Exactly once) — for financial or critical commands
client.publish("payment/authorize", "txn123", qos=2)
# (guaranteed delivery, no duplicates — but 2x round trips)
Fix 2: Duplicate Messages with QoS 1
WRONG — not handling duplicate message IDs:
def on_message(client, userdata, msg):
print(f"Received: {msg.payload}")
# (duplicate messages are processed again — may cause double actions)
RIGHT — deduplicate with message IDs:
processed_ids = set()
def on_message(client, userdata, msg):
# Check for duplicate using message ID
msg_id = f"{msg.topic}:{msg.payload}"
if msg_id in processed_ids:
return # skip duplicate
processed_ids.add(msg_id)
print(f"Received: {msg.payload}")
Fix 3: QoS 2 Protocol Issues
WRONG — broker runs out of memory for pending QoS 2 packets:
# Mosquitto log: "Too many PUBREL packets"
# (publisher sends many QoS 2 messages without waiting for completion)
RIGHT — throttle QoS 2 publishing:
import time
def publish_qos2(client, topic, payload):
# Wait for previous QoS 2 message to complete
while client._out_pending_qos2_count > 5:
time.sleep(0.1)
info = client.publish(topic, payload, qos=2)
info.wait_for_publish() # blocks until PUBCOMP received
Fix 4: Session State Conflicts
WRONG — using clean session=True on reconnect:
client.connect("broker", 1883, 60, clean_session=True)
# (previous QoS 1/2 messages are discarded — lost messages)
RIGHT — use persistent sessions for QoS reliability:
client.connect("broker", 1883, 60, clean_session=False)
# (broker stores pending QoS 1/2 messages for this client)
Fix 5: Client ID and Session Mismatch
client1 = mqtt.Client("sensor1")
client1.connect("broker", 1883, 60, clean_session=False)
# ... messages are queued ...
client2 = mqtt.Client("sensor1") # same ID
client2.connect("broker", 1883, 60, clean_session=True)
# (clean session=True clears the queued messages for client1!)
RIGHT — use unique client IDs or consistent clean_session:
# Each device gets a unique, persistent client ID
client = mqtt.Client(client_id="sensor1_kitchen")
client.connect("broker", 1883, 60, clean_session=False)
Fix 6: Verifying QoS with Mosquitto Test
# Terminal 1 (subscribe with QoS 2):
mosquitto_sub -h localhost -t test -q 2 -v
# Terminal 2 (publish with QoS 2):
mosquitto_pub -h localhost -t test -m "hello" -q 2
# Terminal 1 output:
# test hello
# (if QoS 2 works end-to-end, no duplicates appear)
Use DodaTech's MQTT Reliability Analyzer to measure message delivery rates, detect duplicate messages, and optimize QoS configuration for your IoT deployment.
Prevention
- Use QoS 2 only when exactly-once delivery is required.
- Implement deduplication on subscribers for QoS 1.
- Throttle QoS 2 publishing to avoid broker buffer overflow.
- Use clean_session=False with persistent client IDs.
- Monitor broker logs for PUBREL flooding.
Common Mistakes with qos error
- Forgetting
deriving (Show, Eq)on custom data types needed for debugging - Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable
- Using
headandtailinstead of pattern matching, causing runtime errors on empty lists
These mistakes appear frequently in real-world MQTT code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro