What Is an MQTT Reason Code and Reason String?
Reason codes are the MQTT 5 feature that turns a mystifying silent failure into a diagnosable one. In 3.1.1 a broker that refused your subscription often just gave you nothing useful; in MQTT 5 it tells you why. For a controls engineer commissioning a gateway that will not connect, this is the difference between guessing and knowing. This page explains what reason codes and reason strings are and how to read them during troubleshooting.
MQTT Reason Code and Reason String in one line: An MQTT reason code is a single byte, present on most MQTT 5 acknowledgement and disconnect packets, that states the outcome of an operation, such as success, not authorized, or topic filter invalid. An optional reason string is a human-readable text the server may attach alongside it. Together they explain why a connect, publish, subscribe, or disconnect succeeded or failed, rather than leaving the client to guess.
Where Reason Codes Appear and What They Tell You
Reason codes ride on the acknowledgement packets across the protocol: CONNACK for a connection attempt, PUBACK and PUBREC for a QoS 1 or 2 publish, SUBACK for a subscription, UNSUBACK, and DISCONNECT. Each carries a byte whose value falls in a defined range, where values below 128 generally mean success or a normal outcome and values of 128 and above mean an error. So a SUBACK reason code tells you not just that your subscribe was answered but whether each topic filter was granted, and at what QoS, or refused.
This is a genuine capability gain over MQTT 3.1.1. Under 3.1.1 the CONNACK carried a small set of connection return codes, but a SUBACK could only signal a failure with a single generic value and there was no clean, standardized way for the broker to explain a refused publish or to send a rich reason on disconnect. MQTT 5 fills those gaps, which is one of the practical reasons to prefer it for anything you will have to commission and support in the field. The broader comparison lives in MQTT 3.1.1 versus MQTT 5 for SCADA.
A particularly useful case is the server-initiated DISCONNECT. In MQTT 5 a broker can send a DISCONNECT to a client, with a reason code, before closing the connection, so instead of the socket simply vanishing the client learns it was disconnected because of, for example, an administrative action, a session takeover by another client using the same identifier, or a keepalive timeout. That reason is exactly what you want when diagnosing a client that keeps dropping, because it distinguishes a network fault from a deliberate broker decision.
Reading Reason Strings During Commissioning
The reason string is the optional human-readable companion to the code. Where the code is a fixed byte meant for programmatic handling, the reason string is free text the server may supply to explain the specifics, for example naming which topic filter was rejected or why authorization failed. It is intended for logs and diagnostics, not for the client to parse and branch on. A well-run broker populates it with something an engineer reading a log can act on.
In commissioning, the first thing to do when a gateway will not connect is capture the CONNACK reason code. A not-authorized code points you at credentials or an access control list, not at the network; a code indicating the client identifier is not valid or was taken over points at a duplicate identifier, which is a classic cause of two devices fighting over one session. This is the same diagnostic thread that runs through troubleshooting a Sparkplug node going offline, where a takeover or an auth failure can masquerade as a flaky link.
The discipline is to log the reason code and string at every level, connect, subscribe, and disconnect, rather than only noting that an operation failed. A gateway that logs merely disconnected tells you nothing; one that logs disconnected, reason code keepalive timeout tells you to look at keepalive and the cellular idle timeout. Reason strings do not fix anything by themselves, but they turn a black-box failure into a starting point, which is most of the battle when a site is a long drive away.
Frequently Asked Questions
Are MQTT reason codes available in MQTT 3.1.1?
Only in a limited form. MQTT 3.1.1 has connection return codes on the CONNACK and a single generic failure indication on the SUBACK, but it lacks the broad, consistent reason codes and the optional reason strings that MQTT 5 attaches to publish acknowledgements, unsubscribe, and disconnect. The richer diagnostics of MQTT 5 reason codes are one of the concrete reasons to run version 5 on anything you will have to commission and support remotely.
Should my client branch its logic on the reason string?
No. Branch on the reason code, which is a defined byte with stable meaning across brokers, and treat the reason string purely as human-readable diagnostic text for logs. The reason string is free text a server may or may not supply and may word differently from another broker, so parsing it to drive logic makes your client fragile. Log it verbatim so an engineer can read it, and make programmatic decisions from the numeric reason code.
Sources and verification
This page references the protocol specifications published by the organizations below. Editions, product capabilities, and documentation change over time - confirm current requirements and specifications directly with the source.
- MQTT Version 5.0 (OASIS Standard) - OASIS (v5.0, 2019)
Merobix is not affiliated with, endorsed by, or sponsored by these organizations; their names are used only to identify the standards and products discussed.
Automation services
Need help turning this into a working system?
Merobix integrates SCADA, programs Allen-Bradley and Siemens PLCs, and designs and fabricates industrial control panels.
Meeting requests are reviewed before confirmation.