MQTT GUIDE
8 min readMQTT Reason Codes
A reason code is a single-byte status value MQTT 5.0 attaches to control packets that report an outcome (CONNACK, PUBACK, SUBACK, DISCONNECT and more). Values below 0x80 mean success or a normal outcome; 0x80 and above signal a failure and say why. It replaces MQTT 3.1.1’s near-silent failures with actionable feedback.
In MQTT 3.1.1, when something went wrong the broker often just closed the connection and left you guessing. MQTT 5.0 fixes that with reason codes: a single byte on every acknowledgement that says whether an operation succeeded and, if not, why.
How reason codes work
Most MQTT 5.0 control packets that report an outcome — CONNACK, PUBACK, SUBACK, DISCONNECT, AUTH and more — carry a reason code. The value range is split in two: anything below 0x80 is a success or normal outcome, and anything 0x80 or above is a failure. The sender may also attach an optional reason string with human-readable detail for logs.
Common reason codes
| Code | Name | Typical meaning |
|---|---|---|
0x00 | Success / Normal | The operation succeeded |
0x80 | Unspecified error | Something failed without a more specific code |
0x87 | Not authorized | The client isn’t permitted to do this |
0x97 | Quota exceeded | A broker limit was hit |
Reason codes by packet
Each acknowledgement or control packet defines its own subset of reason codes, and the same code keeps the same meaning wherever it appears. Codes below 0x80 are a success or normal outcome; 0x80 and above signal a failure. Expand a packet below to see every reason code the MQTT 5.0 specification defines for it.
CONNACK
Returned by the broker on the CONNACK that answers a client’s CONNECT.
| Code | Name | Meaning |
|---|---|---|
0x00 | Success | The connection is accepted |
0x80 | Unspecified error | Refused without a more specific reason |
0x81 | Malformed Packet | The CONNECT could not be parsed |
0x82 | Protocol Error | The CONNECT broke a protocol rule |
0x83 | Implementation specific error | Valid packet, but rejected by this broker |
0x84 | Unsupported Protocol Version | The broker doesn’t support the requested MQTT version |
0x85 | Client Identifier not valid | The client ID is well-formed but not accepted |
0x86 | Bad User Name or Password | Credentials were malformed or wrong |
0x87 | Not authorized | The client is not authorized to connect |
0x88 | Server unavailable | The broker is not available |
0x89 | Server busy | The broker is busy; try again later |
0x8A | Banned | The client has been banned by an administrator |
0x8C | Bad authentication method | The requested authentication method isn’t supported |
0x90 | Topic Name invalid | The Will topic is well-formed but not accepted |
0x95 | Packet too large | The CONNECT exceeded the maximum packet size |
0x97 | Quota exceeded | An implementation or administrative quota was exceeded |
0x99 | Payload format invalid | The Will payload doesn’t match its payload format indicator |
0x9A | Retain not supported | The Will asks to be retained, but the broker doesn’t support retained messages |
0x9B | QoS not supported | The Will QoS exceeds what the broker supports |
0x9C | Use another server | The client should temporarily use another broker |
0x9D | Server moved | The client should permanently use another broker |
0x9F | Connection rate exceeded | The connection-rate limit was exceeded |
For contrast, MQTT 3.1.1 defined only a small fixed set of CONNACK return codes — with no reason string, and nothing comparable for other packets:
| Code | MQTT 3.1.1 CONNACK meaning |
|---|---|
0x00 | Connection accepted |
0x01 | Unacceptable protocol version |
0x02 | Identifier rejected |
0x03 | Server unavailable |
0x04 | Bad user name or password |
0x05 | Not authorized |
PUBACK and PUBREC
Both acknowledge a received PUBLISH — PUBACK for QoS 1, PUBREC as the first step for QoS 2 — and share the same reason codes.
| Code | Name | Meaning |
|---|---|---|
0x00 | Success | The message is accepted |
0x10 | No matching subscribers | Accepted, but no subscription matched — it reached no one |
0x80 | Unspecified error | Rejected without a more specific reason |
0x83 | Implementation specific error | Rejected by this broker |
0x87 | Not authorized | The client isn’t authorized to publish to this topic |
0x90 | Topic Name invalid | The topic is well-formed but not accepted |
0x91 | Packet Identifier in use | The packet identifier is already in use (session-state mismatch) |
0x97 | Quota exceeded | A publish quota was exceeded |
0x99 | Payload format invalid | The payload doesn’t match its payload format indicator |
PUBREL and PUBCOMP
The second and final steps of the QoS 2 handshake. Only two codes apply.
| Code | Name | Meaning |
|---|---|---|
0x00 | Success | The QoS 2 handshake continues normally |
0x92 | Packet Identifier not found | No such packet identifier is known (session-state mismatch) |
SUBACK
Answers a SUBSCRIBE with one code per requested topic filter — the granted QoS on success, or why it failed.
| Code | Name | Meaning |
|---|---|---|
0x00 | Granted QoS 0 | Subscription accepted at maximum QoS 0 |
0x01 | Granted QoS 1 | Subscription accepted at maximum QoS 1 |
0x02 | Granted QoS 2 | Subscription accepted at maximum QoS 2 |
0x80 | Unspecified error | Rejected without a more specific reason |
0x83 | Implementation specific error | Rejected by this broker |
0x87 | Not authorized | The client isn’t authorized to subscribe to this topic filter |
0x8F | Topic Filter invalid | The topic filter is malformed |
0x91 | Packet Identifier in use | The packet identifier is already in use |
0x97 | Quota exceeded | A subscription quota was exceeded |
0x9E | Shared Subscriptions not supported | The broker doesn’t support shared subscriptions |
0xA1 | Subscription Identifiers not supported | The broker doesn’t support subscription identifiers |
0xA2 | Wildcard Subscriptions not supported | The broker doesn’t support wildcard subscriptions |
UNSUBACK
Answers an UNSUBSCRIBE with one code per topic filter.
| Code | Name | Meaning |
|---|---|---|
0x00 | Success | The subscription was removed |
0x11 | No subscription existed | No matching subscription was found |
0x80 | Unspecified error | Rejected without a more specific reason |
0x83 | Implementation specific error | Rejected by this broker |
0x87 | Not authorized | The client isn’t authorized to unsubscribe |
0x8F | Topic Filter invalid | The topic filter is malformed |
0x91 | Packet Identifier in use | The packet identifier is already in use |
DISCONNECT
In MQTT 5.0 either side can send a DISCONNECT with a reason code. The Sent by column shows who may use each code.
| Code | Name | Sent by | Meaning |
|---|---|---|---|
0x00 | Normal disconnection | Client or Server | Close cleanly; do not publish the Will |
0x04 | Disconnect with Will Message | Client | Disconnect, but ask the broker to publish the Will |
0x80 | Unspecified error | Client or Server | No specific reason given |
0x81 | Malformed Packet | Client or Server | A packet could not be parsed |
0x82 | Protocol Error | Client or Server | A protocol rule was violated |
0x83 | Implementation specific error | Client or Server | Rejected by the sender’s implementation |
0x87 | Not authorized | Server | The client is not authorized |
0x89 | Server busy | Server | The broker is busy |
0x8B | Server shutting down | Server | The broker is shutting down |
0x8D | Keep Alive timeout | Server | No packet arrived within 1.5× the keep-alive |
0x8E | Session taken over | Server | Another connection used the same client ID |
0x8F | Topic Filter invalid | Server | A subscription topic filter is malformed |
0x90 | Topic Name invalid | Client or Server | A topic name is malformed |
0x93 | Receive Maximum exceeded | Client or Server | Too many unacknowledged PUBLISHes — see flow control |
0x94 | Topic Alias invalid | Client or Server | A topic alias exceeded the maximum |
0x95 | Packet too large | Client or Server | A packet exceeded the maximum size |
0x96 | Message rate too high | Client or Server | The message rate was too high |
0x97 | Quota exceeded | Client or Server | A quota was exceeded |
0x98 | Administrative action | Client or Server | Closed by an administrative action |
0x99 | Payload format invalid | Client or Server | A payload didn’t match its payload format indicator |
0x9A | Retain not supported | Server | The broker doesn’t support retained messages |
0x9B | QoS not supported | Server | The requested QoS isn’t supported |
0x9C | Use another server | Server | Temporarily connect to another broker |
0x9D | Server moved | Server | Permanently connect to another broker |
0x9E | Shared Subscriptions not supported | Server | The broker doesn’t support shared subscriptions |
0x9F | Connection rate exceeded | Server | The connection-rate limit was exceeded |
0xA0 | Maximum connect time | Server | The maximum connection time was exceeded |
0xA1 | Subscription Identifiers not supported | Server | The broker doesn’t support subscription identifiers |
0xA2 | Wildcard Subscriptions not supported | Server | The broker doesn’t support wildcard subscriptions |
AUTH
Drives MQTT 5.0 enhanced authentication (for example SCRAM), exchanged between client and broker.
| Code | Name | Meaning |
|---|---|---|
0x00 | Success | Authentication completed successfully |
0x18 | Continue authentication | More data is needed to finish authenticating |
0x19 | Re-authenticate | The client asks to re-authenticate on the live connection |
Reason codes in TBMQ
TBMQ returns MQTT 5.0 reason codes across its acknowledgement packets — CONNACK, PUBACK, SUBACK, DISCONNECT and more — so a client learns exactly why a connect, publish, or subscribe was refused rather than just seeing the connection drop. See the MQTT protocol guide for supported 5.0 behavior.
Frequently asked questions
What is a reason code in MQTT?
A single-byte value in an MQTT 5.0 acknowledgement packet that reports the outcome of an operation. Codes below 0x80 are success or normal outcomes; codes 0x80 and above indicate a failure and identify what went wrong.
Which packets carry reason codes?
CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, UNSUBACK, DISCONNECT and AUTH. SUBACK carries one reason code per topic filter in the request.
What is a reason string?
An optional human-readable text field the sender can add next to the reason code to give more detail, meant for logging and diagnostics rather than for programmatic decisions.
Do reason codes exist in MQTT 3.1.1?
Not in the same way. MQTT 3.1.1 has a few CONNACK return codes and per-subscription SUBACK results, but a failed publish or an unexpected disconnect gave almost no feedback — often the broker just closed the connection. MQTT 5.0 adds reason codes across the board.
What does reason code 0x87 mean?
0x87 is “Not authorized”. For example, a broker returns it on PUBACK or SUBACK when a client tries to publish or subscribe to a topic its permissions don’t allow.
Run it yourself
TBMQ is a free, open-source MQTT broker built to scale. Spin it up in minutes or try the live demo — no install required.