MQTT Fundamentals

MQTT 5 Reason Codes: Complete Reference Table with Causes and Fixes

Every MQTT 5 reason code in one table, grouped by the packets that carry it, with the usual causes and practical fixes. Includes the mapping from MQTT 3.1.1 return codes and Python code to log reason codes properly.

Reference table of hexadecimal mqtt reason codes grouped by packet type on a debugging console
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

MQTT reason codes are the single-byte status values that MQTT 5 brokers and clients put into acknowledgements and DISCONNECT packets to say exactly what happened. This reference lists every code, which packets can carry it, what usually triggers it and how to fix it, so you can turn a cryptic 0x87 or 0x97 in your logs into a concrete next step.

How MQTT Reason Codes Work

MQTT 3.1.1 had only a handful of status values: six CONNACK return codes and a single failure value (0x80) in SUBACK. Everything else, such as a rejected publish or a broker-side disconnect, was silent; the connection just closed. MQTT 5 replaced this with a shared set of reason codes used across CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, UNSUBACK, DISCONNECT and AUTH. It is one of the most practical MQTT 5 features for anyone operating devices in the field.

Two rules make the table easy to read:

  • Values below 0x80 mean success or a non-error outcome, such as "granted QoS 1" or "no matching subscribers".
  • Values of 0x80 and above mean failure. The packet that carries the code tells you which operation failed.

Many packets can also carry a Reason String property, a human-readable explanation from the sender. Always log it when present; it often contains the exact limit that was hit.

Success and Informational Codes

Code Name Packets What it means
0x00 Success / Normal disconnection / Granted QoS 0 CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, UNSUBACK, AUTH, DISCONNECT, SUBACK Operation succeeded; in SUBACK it means the subscription was granted at QoS 0
0x01 Granted QoS 1 SUBACK Subscription accepted with maximum QoS 1
0x02 Granted QoS 2 SUBACK Subscription accepted with maximum QoS 2
0x04 Disconnect with Will Message DISCONNECT Client is leaving cleanly but asks the broker to publish its Will
0x10 No matching subscribers PUBACK, PUBREC Publish accepted, but nobody was subscribed
0x11 No subscription existed UNSUBACK You unsubscribed from a filter you were not subscribed to
0x18 Continue authentication AUTH Enhanced authentication exchange continues
0x19 Re-authenticate AUTH Client starts re-authentication on an existing connection

A SUBACK granting a lower QoS than you requested is not an error. If you ask for QoS 2 and get 0x01, the broker's maximum QoS is 1 for your connection, and messages will arrive at QoS 1 at most.

Error Reason Codes Reference

Code Name Packets Usual cause Fix
0x80 Unspecified error All acknowledgements, DISCONNECT Broker declined without a specific reason Read the Reason String and broker logs
0x81 Malformed Packet CONNACK, DISCONNECT Invalid encoding, bad UTF-8, wrong lengths Update the client library; check custom encoders
0x82 Protocol Error CONNACK, DISCONNECT Packet valid but sent in the wrong order or with forbidden fields Check for duplicate CONNECT or misuse of properties
0x83 Implementation specific error CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT Broker-specific policy rejected the operation Check broker documentation and Reason String
0x84 Unsupported Protocol Version CONNACK Broker does not speak the requested version Use MQTT 3.1.1 or 5.0 as supported
0x85 Client Identifier not valid CONNACK Empty, too long or disallowed client ID Use a valid, unique client ID
0x86 Bad User Name or Password CONNACK Wrong or malformed credentials Re-copy username and password
0x87 Not authorized CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT Credentials or client lack permission Check credentials, client ID binding and topic rights
0x88 Server unavailable CONNACK Broker cannot accept connections right now Retry with backoff
0x89 Server busy CONNACK, DISCONNECT Broker is overloaded Retry later with backoff and jitter
0x8A Banned CONNACK Client or address is blocked Stop retry loops; fix credentials; contact the operator
0x8B Server shutting down DISCONNECT Planned shutdown or restart Reconnect with backoff
0x8C Bad authentication method CONNACK, DISCONNECT Enhanced auth method not supported Remove or correct the Authentication Method
0x8D Keep Alive timeout DISCONNECT No packet received within 1.5 x keep alive Service the network loop; check connectivity
0x8E Session taken over DISCONNECT Another client connected with the same client ID Give every client a unique ID
0x8F Topic Filter invalid SUBACK, UNSUBACK, DISCONNECT Malformed filter, e.g. a/#/b Fix wildcard placement
0x90 Topic Name invalid CONNACK, PUBACK, PUBREC, DISCONNECT Wildcards or invalid characters in a publish topic Publish to a concrete topic name
0x91 Packet Identifier in use PUBACK, PUBREC, SUBACK, UNSUBACK Packet ID reused while still in flight Library bug or custom packet ID handling
0x92 Packet Identifier not found PUBREL, PUBCOMP QoS 2 state lost, often after a session reset Check session persistence settings
0x93 Receive Maximum exceeded DISCONNECT Too many unacknowledged QoS 1/2 publishes Respect the peer's Receive Maximum
0x94 Topic Alias invalid DISCONNECT Alias out of range or undefined Reset aliases on reconnect; honour the maximum
0x95 Packet too large CONNACK, DISCONNECT Packet exceeds the Maximum Packet Size Shrink or chunk payloads
0x96 Message rate too high DISCONNECT Publishing faster than allowed Throttle publishes
0x97 Quota exceeded CONNACK, PUBACK, PUBREC, SUBACK, DISCONNECT A plan or account limit was reached Reduce usage or upgrade the plan
0x98 Administrative action DISCONNECT An operator disconnected the client Check with the broker administrator
0x99 Payload format invalid CONNACK, PUBACK, PUBREC, DISCONNECT Payload Format Indicator says UTF-8 but bytes are not Fix encoding or clear the indicator
0x9A Retain not supported CONNACK, DISCONNECT Retain flag used where not allowed Publish without retain
0x9B QoS not supported CONNACK, DISCONNECT Publish QoS above the broker's maximum Lower the QoS to the advertised maximum
0x9C Use another server CONNACK, DISCONNECT Broker redirects the client temporarily Follow the Server Reference property
0x9D Server moved CONNACK, DISCONNECT Broker redirects permanently Update the configured host
0x9E Shared Subscriptions not supported SUBACK, DISCONNECT $share/... filter not allowed Use a plan or broker with shared subscriptions
0x9F Connection rate exceeded CONNACK Too many connection attempts Add backoff to reconnect logic
0xA0 Maximum connect time DISCONNECT Connection lifetime limit reached Reconnect; expect periodic disconnects
0xA1 Subscription Identifiers not supported SUBACK, DISCONNECT Subscription Identifier property not allowed Remove the property
0xA2 Wildcard Subscriptions not supported SUBACK, DISCONNECT Wildcards not allowed for this client Subscribe to explicit topics

Note that some codes, such as 0x8D and 0x8E, appear only in a DISCONNECT sent by the broker. Others, like 0x84 to 0x88, can only appear in CONNACK because they describe why the connection was refused in the first place.

Mapping MQTT 3.1.1 Return Codes to MQTT 5

If you support both protocol versions, this mapping keeps your error handling consistent. The detailed walkthrough is in the guide to MQTT CONNACK return codes.

MQTT 3.1.1 CONNACK MQTT 5 equivalent
0 Connection accepted 0x00 Success
1 Unacceptable protocol version 0x84 Unsupported Protocol Version
2 Identifier rejected 0x85 Client Identifier not valid
3 Server unavailable 0x88 Server unavailable
4 Bad user name or password 0x86 Bad User Name or Password
5 Not authorized 0x87 Not authorized

Logging MQTT Reason Codes in Python

With paho-mqtt 2.x and CallbackAPIVersion.VERSION2, every callback receives a ReasonCode object. Its string form is the spec name and .value is the numeric code, which is exactly what you want in logs:

import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, reason_code, properties):
    print(f"CONNACK: {reason_code} (0x{reason_code.value:02X})")
    if reason_code.is_failure:
        return
    client.subscribe("connectmq/demo/b7d2/#", qos=2)

def on_subscribe(client, userdata, mid, reason_code_list, properties):
    for rc in reason_code_list:
        print(f"SUBACK: {rc} (0x{rc.value:02X})")

def on_disconnect(client, userdata, flags, reason_code, properties):
    reason = getattr(properties, "ReasonString", None) if properties else None
    print(f"DISCONNECT: {reason_code} (0x{reason_code.value:02X}) {reason or ''}")

client = mqtt.Client(
    mqtt.CallbackAPIVersion.VERSION2,
    client_id="reason-demo-b7d2",
    protocol=mqtt.MQTTv5,
)
client.on_connect = on_connect
client.on_subscribe = on_subscribe
client.on_disconnect = on_disconnect
client.connect("broker.connectmq.com", 1883, keepalive=30)
client.loop_forever()

Run this against the public MQTT test broker and you will see Success on connect and a granted QoS in SUBACK. Try subscribing to an invalid filter such as connectmq/demo/b7d2/#/x to see 0x8F Topic Filter invalid in action. With MQTT.js, the same information is in connack.reasonCode, the granted array of the subscribe callback, and the disconnect event packet.

Debugging Workflow by Reason Code

A practical order for investigating a failure:

  1. Identify the packet. A code in CONNACK is a connection problem; in SUBACK, a subscription problem; in DISCONNECT, a runtime problem.
  2. Read the Reason String. It usually narrows the cause to a specific limit or rule.
  3. Check credentials and identity first for 0x85, 0x86 and 0x87. The guide to fixing MQTT connection refused: not authorized covers this in depth.
  4. Check limits for 0x93, 0x95, 0x96 and 0x97: message size, rate, connection count and in-flight messages.
  5. Check fleet hygiene for 0x8E: duplicate client IDs are the most common cause of disconnect loops.

Reason Codes on ConnectMQ

ConnectMQ supports MQTT 3.1.1 and 5.0, so MQTT 5 clients receive the reason codes above. Plan quotas surface as 0x97 Quota exceeded, for example when a free Serverless broker already has its maximum of 25 connections. Plan features matter too: the free plan grants QoS up to 1, so a QoS 2 subscription is granted at a lower QoS in SUBACK, while QoS 2 and shared subscriptions come with the paid Serverless plan. Repeated failed logins are treated as suspicious, and the source IP can be blocked, so never leave a client retrying bad credentials in a tight loop.

For background on what an MQTT broker does with these codes, or the full protocol in context, see the MQTT tutorial.

Debug Faster with ConnectMQ

A managed MQTT broker with an in-browser client and live topic explorer makes it much quicker to reproduce a failing publish or subscription. Create a free Serverless broker at app.connectmq.com, no credit card needed, and test your error handling against real MQTT 5 responses.

Frequently Asked Questions

What is the difference between a reason code and a return code?

Return code is the MQTT 3.1.1 term for the six CONNACK values. MQTT 5 generalised the idea into reason codes that appear in almost every acknowledgement and in DISCONNECT.

Which reason codes mean success?

Any value below 0x80. That includes 0x00 Success, 0x01 and 0x02 for granted QoS, and informational values such as 0x10 No matching subscribers.

What does MQTT reason code 0x97 mean?

0x97 is Quota exceeded: the client hit a limit such as maximum connections or a plan restriction. Reduce usage or move to a plan with higher limits.

Do MQTT 3.1.1 clients receive MQTT 5 reason codes?

No. A 3.1.1 connection only gets the classic CONNACK return codes and the 0x80 SUBACK failure value. Connect with protocol version 5 to receive detailed reason codes.

Related articles

Why Your MQTT Client Keeps Disconnecting (and How to Fix It)

Reconnect loops are one of the most common MQTT problems. Learn the five usual causes, from duplicate client IDs to keep alive timeouts and oversized packets, and follow a diagnosis flow that pinpoints yours.

8 min read

Who Invented MQTT? The History from Oil Pipelines to IoT

MQTT was invented in 1999 by Andy Stanford-Clark of IBM and Arlen Nipper of Arcom to monitor oil pipelines over satellite. Follow its path to open source, OASIS, ISO and today's managed cloud brokers.

7 min read

MQTT Maximum Message Size: Limits and Large Payload Strategies

MQTT can technically carry a 256 MB packet, but brokers, client libraries and devices impose far lower limits. Learn where the real limits are, what happens when you exceed them, and how to send large payloads.

7 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.