The MQTT client ID is the identifier a client sends in CONNECT, and the broker uses it to tell connections and sessions apart. This guide explains why two clients with the same ID keep disconnecting each other, how to design ID formats that scale from one prototype to a fleet, and how to diagnose takeovers quickly when testing on a public broker.
What the MQTT Client ID Is Used For
Every CONNECT packet carries a Client Identifier field. The MQTT broker uses it for three things:
- Connection uniqueness. Only one network connection per client ID may be active at a time.
- Session lookup. Persistent session state (subscriptions and queued messages) is stored under the client ID.
- Operations. Logs, connection events and monitoring topics refer to clients by ID.
It is not an authentication credential by itself. Anyone who can connect can claim any ID unless the broker binds IDs to credentials. On ConnectMQ private brokers, client IDs are bound to the credentials issued in the portal, so a different user cannot reuse your device's ID.
Why Duplicate Client IDs Kick Each Other Off
The specification is explicit: if a client connects with an ID that is already connected, the broker must disconnect the existing client. This is called a session takeover. It exists so that a device that silently lost its connection can reconnect immediately without waiting for the old socket to time out.
The problem appears when two different clients share an ID. The sequence goes like this:
- Client A connects as
sensor-01. - Client B connects as
sensor-01. The broker disconnects A. - A's auto-reconnect fires and connects as
sensor-01. The broker disconnects B. - B reconnects... and the loop repeats every few seconds.
Symptoms include constant connect/disconnect logs, missed messages, will messages fired repeatedly, and an "online/offline" indicator that flickers. In MQTT 5 the kicked client receives a DISCONNECT with reason code 0x8E (Session taken over), which makes diagnosis much easier than in 3.1.1, where the socket simply closes.
Common Causes
- The same firmware image flashed to many boards with a hard-coded ID.
- Copy-pasted example code with an ID such as
testoresp32client. - A service running in several containers or pods with the same configured ID.
- A developer running the same script in two terminals.
- A desktop MQTT tool and a device both using the device's ID.
Designing an MQTT Client ID Format
Good IDs are unique, stable, readable and safe for every broker.
Format Rules
| Rule | Why |
|---|---|
Use only a-z, A-Z, 0-9, - and _ |
Every broker must accept these characters |
| Keep it short, ideally 23 characters or fewer | MQTT 3.1.1 only guarantees support up to 23 bytes; most brokers allow more |
| Never leave it empty in production | Empty IDs prevent persistent sessions, and many brokers reject them |
| Make it stable for devices with sessions | A changing ID means a new session every time |
| Make it unique per running instance | Prevents takeover loops |
| Avoid secrets and personal data | IDs appear in logs and monitoring |
Patterns That Work
- Devices: a prefix plus a hardware-derived identifier, such as
env-plus the last bytes of the MAC address (env-a4cf12b3), or a serial number from your manufacturing database. - Backend services: service name plus instance identifier, such as
alarm-worker-0from a StatefulSet ordinal. If instances are fully interchangeable and do not need sessions, a random suffix is fine. - Browser dashboards: a prefix plus a random string per tab, with clean sessions, because nobody needs to resume a dashboard's session.
- Test scripts: a random suffix every run, for example
test-plus 6 random hex characters.
Generating IDs in Code
Python, using the MAC address for a stable device ID or a random token for test clients:
import secrets
import uuid
import paho.mqtt.client as mqtt
def device_client_id(prefix: str = "env") -> str:
mac = uuid.getnode() # 48-bit hardware address as an int
return f"{prefix}-{mac:012x}" # e.g. env-a4cf12b3c4d5
def test_client_id(prefix: str = "test") -> str:
return f"{prefix}-{secrets.token_hex(3)}" # e.g. test-9f2c1a
client_id = device_client_id()
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id=client_id,
protocol=mqtt.MQTTv5)
print("Using client ID:", client_id)
ESP32 with PubSubClient, deriving the ID from the efuse MAC:
#include <WiFi.h>
#include <PubSubClient.h>
WiFiClient net;
PubSubClient mqttClient(net);
String makeClientId() {
uint64_t mac = ESP.getEfuseMac();
char id[24];
snprintf(id, sizeof(id), "env-%04x%08x",
(uint16_t)(mac >> 32), (uint32_t)mac);
return String(id);
}
void connectMqtt() {
String clientId = makeClientId();
mqttClient.setServer("broker.connectmq.com", 1883);
while (!mqttClient.connected()) {
if (mqttClient.connect(clientId.c_str())) {
Serial.println("Connected as " + clientId);
} else {
Serial.printf("Failed, state=%d. Retrying in 5 s\n", mqttClient.state());
delay(5000);
}
}
}
The full board setup, including Wi-Fi, is in the ESP32 MQTT tutorial.
Empty Client IDs and Assigned Identifiers
MQTT 3.1.1 allows a zero-length client ID only with clean session set to true; the broker then invents one internally. MQTT 5 goes further: the broker returns an Assigned Client Identifier in CONNACK so the client knows its name. This is handy for throwaway clients, but it rules out persistent sessions, because you cannot reconnect as an identity you do not control. Many brokers, including the ConnectMQ public broker, simply require a non-empty ID, which is the safer habit anyway.
How to Detect a Client ID Takeover
Here is a reproducible test on the public MQTT test broker. Use your own random suffix and remember that the public area is readable by everyone.
- In terminal one, connect with an ID and debug output:
mosquitto_sub -h broker.connectmq.com -p 1883 -V mqttv5 \
-i dup-demo-k7q1 -d -t 'connectmq/demo/k7q1/#'
- In terminal two, run the exact same command.
- Terminal one is disconnected immediately; with
-dyou can see the broker close its connection. Depending on your mosquitto version it either exits or reconnects, in which case the two terminals start kicking each other off, which is exactly the loop described above. - In a third terminal, watch the broker's view of that client:
mosquitto_sub -h broker.connectmq.com -p 1883 -i watcher-k7q1 -v \
-t '$SYS/broker/connection/dup-demo-k7q1/state'
In production, a quick checklist when devices flap:
- Collect client IDs from all suspect devices and look for duplicates.
- Check whether disconnect reason codes show 0x8E (session taken over).
- Compare connection state changes with each device's reconnect timestamps; alternating patterns reveal a pair fighting over one ID.
- Close desktop tools or VS Code sessions that might be using a device's ID.
- If flapping continues with unique IDs, look at keep alive and network timeouts instead.
MQTT Client IDs in Production
A few operational habits prevent most client ID incidents. Generate IDs at provisioning time rather than at runtime, so the same device always gets the same ID even after a factory reset. Store the ID next to the device's credentials so support staff can match log entries to hardware. When you replace a broken unit, decide deliberately whether the new device inherits the old ID (and its session) or gets a fresh one. For services, make the instance identifier come from your orchestrator rather than from a config file that every replica shares.
As fleets grow, treat client IDs as part of your provisioning process: record them alongside serial numbers, derive them from hardware, and never reuse an ID for a different physical device. If you are learning MQTT from scratch, the MQTT tutorial explains how IDs, sessions and topics fit together.
Manage Client IDs Safely with ConnectMQ
ConnectMQ is a fully managed MQTT service that issues a username, password and client ID for each broker you create, and binds client IDs to those credentials so other tenants cannot hijack them. Connection state is visible live in the portal, which makes takeover loops easy to spot. Create a free Serverless broker with no credit card and connect using YOUR_USERNAME, YOUR_PASSWORD and YOUR_CLIENT_ID.
Frequently Asked Questions
What happens if two MQTT clients use the same client ID?
The broker disconnects the client that was already connected and keeps the new one. If both clients auto-reconnect, they keep kicking each other off in a loop.
How long can an MQTT client ID be?
MQTT 3.1.1 guarantees support for 1 to 23 bytes of letters and digits, and brokers may allow longer IDs and more characters. Keeping IDs short and alphanumeric with hyphens is the most portable choice.
Should the MQTT client ID be random?
Use a random ID for short-lived or interchangeable clients that do not need sessions. Devices and services that rely on persistent sessions need a stable, unique ID that stays the same across reconnects.
Is the client ID a security credential?
No. Treat it as an identifier only and always authenticate with a username and password or certificates; brokers that bind IDs to credentials, as ConnectMQ does, add protection against impersonation.



