Skip to content

How to Subscribe to MQTT Topics

DodaTech Updated 2026-06-24 3 min read

In this tutorial, you'll learn about How to Subscribe to MQTT Topics. We cover key concepts, practical examples, and best practices.

The Problem

Your MQTT client subscribes to a topic but never receives messages. You verified the publisher is working, yet on_message is never called. The broker log shows the subscription but messages are not delivered.

Quick Fix

Fix 1: Topic Mismatch

WRONG — subscribing to the wrong topic:

import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, rc):
    print("Connected")
    client.subscribe("sensor/temperature")  # publisher sends to "sensors/temperature"

def on_message(client, userdata, msg):
    print(msg.payload)

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("localhost", 1883, 60)
client.loop_forever()
# (no messages received — topic path does not match)

RIGHT — match the exact topic:

client.subscribe("sensors/temperature")
# Now messages published to "sensors/temperature" are received

Fix 2: Wildcard Subscription

WRONG — using + (single-level) where # (multi-level) is needed:

client.subscribe("sensors/+")   # matches "sensors/temp" but NOT "sensors/floor1/temp"
client.subscribe("sensors/#")   # matches both "sensors/temp" AND "sensors/floor1/temp"

RIGHT — understand the wildcard semantics:

# "+" matches exactly one level
client.subscribe("sensors/+/temperature")
# Matches: sensors/kitchen/temperature, sensors/livingroom/temperature
# Does NOT match: sensors/temperature (need two levels)

# "#" matches all remaining levels (must be at the end)
client.subscribe("sensors/#")
# Matches: sensors/temperature, sensors/kitchen/temp, sensors/floor1/room1/humidity

Fix 3: QoS Mismatch

WRONG — publisher uses QoS 2 but subscriber uses QoS 0:

# Publisher:
client.publish("sensors/temp", "23.5", qos=2)

# Subscriber:
client.subscribe("sensors/temp", qos=0)
# (subscriber receives the message at QoS 0 — broker downgrades delivery)

RIGHT — subscribe with the same or higher QoS:

client.subscribe("sensors/temp", qos=2)
# (messages are delivered at the subscribed QoS level)

Fix 4: Not Looping (No Network Processing)

WRONG — subscribing but not calling loop_start or loop_forever:

client.connect("localhost", 1883, 60)
client.subscribe("sensors/temp")
# (no loop — the client never processes incoming network data)

RIGHT — start the network loop:

client.connect("localhost", 1883, 60)
client.subscribe("sensors/temp")
client.loop_start()  # starts a background thread for network processing
# or
client.loop_forever()  # blocks and processes network traffic

Fix 5: Message Handler Not Registered

WRONG — defining on_message after connecting:

client.connect("localhost", 1883, 60)
client.on_message = on_message  # too late — messages may arrive before handler is set

RIGHT — set callbacks before connecting:

client.on_connect = on_connect
client.on_message = on_message
client.connect("localhost", 1883, 60)

Fix 6: Retained Messages

WRONG — expecting a retained message without subscribing first:

# Publisher sends: client.publish("sensors/temp", "23.5", retain=True)
# Subscriber connects, subscribes, and expects the retained message
# (retained message is delivered after subscription — this works correctly)

If you do not want retained messages, publish with retain=False.

Use DodaTech's MQTT Topic Explorer to subscribe to multiple topics simultaneously, view message hierarchies, and debug wildcard matching.

Prevention

  • Double-check topic spelling and separator (/ vs .).
  • Test with a simple topic before using wildcards.
  • Set callbacks before connecting.
  • Call loop_start() for background processing.
  • Match QoS between publisher and subscriber for reliable delivery.

Common Mistakes with client subscribe

  1. Misunderstanding that String is [Char] with poor performance for large text operations
  2. Using foldl instead of foldl' causing stack overflow on large lists
  3. Forgetting deriving (Show, Eq) on custom data types needed for debugging

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

### Why does my MQTT client receive duplicate messages?

The subscriber subscribed with QoS 1 or 2 but the acknowledgment was lost, causing the broker to resend. This is normal behavior for QoS 1 (at least once). Use QoS 0 for fire-and-forget, or deduplicate messages on the client side with a message ID.

Can I subscribe to multiple topics in one call?

Yes, pass a list of tuples: client.subscribe([("sensors/temp", 1), ("sensors/humidity", 1)]). Both topics are subscribed in a single SUBSCRIBE packet, reducing network overhead.

What happens to messages published before a client subscribes?

By default, they are lost unless the publisher used retain=True. The broker does not queue messages for offline subscribers unless a session is explicitly set with client.connect("host", port, 60) where the clean_session parameter is False.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro