MQTT Fundamentals

MQTT CONNACK Return Codes 0-5 Explained (MQTT 3.1.1)

MQTT 3.1.1 brokers answer every CONNECT with a CONNACK return code from 0 to 5. Learn what each one means, how it looks in Mosquitto, Paho and MQTT.js, and the fastest fix for each refusal.

Terminal output listing the six mqtt connack return codes next to a broker accepting and refusing connections
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 CONNACK return codes are the six values, 0 to 5, that an MQTT 3.1.1 broker sends back to tell a client whether its connection was accepted and, if not, why. This guide explains each code, shows how it appears in common tools and libraries, and gives you a fix you can apply in minutes, with examples against the public test broker and a private broker.

What the CONNACK Packet Contains

After a client opens a TCP connection, its first packet must be CONNECT. The broker replies with CONNACK, a fixed four-byte packet in MQTT 3.1.1:

20 02 00 05
|  |  |  |
|  |  |  +-- Connect return code (here 5 = Not authorized)
|  |  +----- Connect acknowledge flags (bit 0 = Session Present)
|  +-------- Remaining length (always 2)
+----------- Packet type 2 (CONNACK), flags 0

The Session Present flag tells the client whether the broker resumed a stored session, which matters for persistent sessions. The return code is the part this article is about. If it is anything other than 0, the broker closes the network connection right after sending CONNACK, and the client must fix the cause before trying again. For the full byte layout of this and other packets, see the guide to MQTT packet structure.

MQTT CONNACK Return Codes at a Glance

Code Name Typical trigger First thing to check
0 Connection accepted Everything is fine Nothing
1 Unacceptable protocol version Client requested a version the broker does not support Protocol version setting
2 Identifier rejected Client ID empty, invalid or not allowed Client ID value
3 Server unavailable Broker cannot serve the connection right now Broker status, retry with backoff
4 Bad user name or password Credentials wrong or malformed Username and password
5 Not authorized Client is not permitted to connect Credentials, client ID, account

Values 6 to 255 are reserved in MQTT 3.1.1. If your library reports one of them, it is usually decoding an MQTT 5 reason code by mistake, which means the client and broker disagree on protocol version.

How Each Return Code Appears in Your Tools

Error messages differ by tool, which makes searching for them confusing. Here is how the same codes look in three common clients:

Code mosquitto_pub / mosquitto_sub Paho Python 2.x reason_code MQTT.js error message
1 Connection Refused: unacceptable protocol version Unsupported protocol version Connection refused: Unacceptable protocol version
2 Connection Refused: identifier rejected Client identifier not valid Connection refused: Identifier rejected
3 Connection Refused: broker unavailable Server unavailable Connection refused: Server unavailable
4 Connection Refused: bad user name or password Bad user name or password Connection refused: Bad username or password
5 Connection Refused: not authorised Not authorized Connection refused: Not authorized

Paho maps 3.1.1 codes onto MQTT 5 reason code names internally, so you may see the MQTT 5 wording even on a 3.1.1 connection. The MQTT reason codes reference shows the full mapping.

Code 1: Unacceptable Protocol Version

The CONNECT packet carries a protocol level byte: 4 for MQTT 3.1.1, 5 for MQTT 5.0 and 3 for the old MQTT 3.1. If the broker does not support the requested level, it answers with code 1.

How to fix it:

  • Set the protocol version explicitly instead of relying on library defaults. In Mosquitto tools use -V mqttv311 or -V mqttv5, in Paho use protocol=mqtt.MQTTv311, in MQTT.js use protocolVersion: 4 or 5.
  • Avoid MQTT 3.1 (level 3). Modern managed brokers such as ConnectMQ support 3.1.1 and 5.0.
  • If you are behind a proxy or gateway, confirm it passes MQTT through unchanged.

Code 2: Identifier Rejected

The broker did not accept the client identifier. Common reasons:

  • Empty client ID with clean session off. MQTT 3.1.1 only allows a zero-length client ID together with clean session, and many brokers reject empty IDs altogether. Always set a non-empty ID.
  • Characters or length outside the broker's rules. Stick to letters, digits and hyphens, and keep IDs reasonably short.
  • Client ID tied to credentials. Some brokers bind a client ID to a specific account. On a private ConnectMQ broker, client IDs are bound to the credentials issued in the portal, so use the client ID shown there together with its username.

On the public test broker, any non-empty, unique ID works. The MQTT client ID guide covers naming strategies for fleets.

Code 3: Server Unavailable

The network connection worked, but the broker cannot serve MQTT right now. This is usually transient: a restart, overload or maintenance. Implement exponential backoff with jitter rather than reconnecting immediately; thousands of devices reconnecting in lockstep can keep a recovering broker overloaded.

Code 4: Bad User Name or Password

The broker checked the credentials and they did not match. In practice:

  • Re-copy the username and password from the source of truth; trailing spaces and swapped fields are common.
  • Check that your library actually sends the password. Some APIs require username_pw_set() before connect().
  • Watch for special characters in passwords being altered by shell quoting. Wrap them in single quotes on the command line.
  • Do not loop on failures. Brokers commonly protect themselves against brute-force attempts, and ConnectMQ blocks an IP address after repeated failed logins.

Code 5: Not Authorized

The broker understood who you are but refuses the connection. Causes include using credentials for a different broker, a mismatched client ID, a disabled account or an exhausted quota. This is the most common refusal on private brokers, and it has its own step-by-step checklist in fixing MQTT connection refused: not authorized.

Reproducing CONNACK Codes Safely

Seeing the codes yourself makes them easier to recognise in production logs. First, a successful connection to the public MQTT test broker, with debug output enabled:

mosquitto_sub -h broker.connectmq.com -p 1883 -i connack-test-4e8b \
  -t "connectmq/demo/4e8b/#" -V mqttv311 -d

The -d flag prints Client connack-test-4e8b received CONNACK (0), the return code in parentheses. Against a private broker, a deliberately wrong password shows the refusal:

mosquitto_sub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
  -u YOUR_USERNAME -P wrong-password -i YOUR_CLIENT_ID \
  -t "test/#" -V mqttv311 -d

Run that only once or twice; repeated failures can trigger IP blocking.

Handling Return Codes in Python

This paho-mqtt 2.x client connects with MQTT 3.1.1, logs the code and stops instead of retrying on errors that need human attention:

import paho.mqtt.client as mqtt

# Paho maps 3.1.1 codes 1, 2, 4, 5 to MQTT 5 values 0x84, 0x85, 0x86, 0x87
FATAL = {0x84, 0x85, 0x86, 0x87}

def on_connect(client, userdata, flags, reason_code, properties):
    if not reason_code.is_failure:
        print("Connected, session present:", flags.session_present)
        client.subscribe("connectmq/demo/4e8b/#")
        return
    print(f"Connection refused: {reason_code}")
    if reason_code.value in FATAL:
        print("Configuration problem, not retrying")
        client.disconnect()
        client.loop_stop()

client = mqtt.Client(
    mqtt.CallbackAPIVersion.VERSION2,
    client_id="connack-py-4e8b",
    protocol=mqtt.MQTTv311,
)
client.on_connect = on_connect
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_forever()

Handling Return Codes in Node.js

MQTT.js emits an error event with a code property holding the numeric CONNACK value:

const mqtt = require("mqtt");

const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
  clientId: "YOUR_CLIENT_ID",
  username: "YOUR_USERNAME",
  password: "YOUR_PASSWORD",
  protocolVersion: 4,
  reconnectPeriod: 5000,
});

client.on("connect", () => console.log("CONNACK 0: connected"));

client.on("error", (err) => {
  console.error(`CONNACK ${err.code}: ${err.message}`);
  if ([1, 2, 4, 5].includes(err.code)) {
    client.end(true); // stop reconnecting on configuration errors
  }
});

A Quick CONNACK Troubleshooting Checklist

  1. Can you reach the host and port at all? A timeout means no CONNACK was ever sent; check DNS, firewall and port.
  2. Are you using TLS on 8883 and plain TCP on 1883, not the other way round?
  3. Is the protocol version explicit and supported?
  4. Is the client ID non-empty, unique and the one issued with your credentials?
  5. Are username and password exactly as issued?
  6. Does your reconnect logic stop on codes 1, 2, 4 and 5 instead of hammering the broker?

Get Clear Connection Feedback with ConnectMQ

ConnectMQ is a fully managed MQTT service that supports MQTT 3.1.1 and 5.0 on TCP 1883, TLS 8883 and WebSockets. The portal generates matching username, password and client ID for each broker, which removes the most common cause of codes 2, 4 and 5. Create a free Serverless broker at app.connectmq.com, no credit card required. New to brokers? Start with what an MQTT broker does or the complete MQTT tutorial.

Frequently Asked Questions

What does CONNACK return code 0 mean?

Connection accepted. The broker has accepted the CONNECT, and the Session Present flag in the same packet tells you whether a previous session was resumed.

What is the difference between return codes 4 and 5?

Code 4 means the username or password did not match. Code 5 means the broker refuses the client for another authorization reason, such as a client ID not bound to those credentials or an account that is not allowed to connect.

Why does my client get no CONNACK at all?

If no CONNACK arrives, the problem is below MQTT: wrong host or port, a firewall, or a TLS mismatch such as plain MQTT sent to port 8883. Fix connectivity before looking at return codes.

Does MQTT 5 use the same CONNACK codes?

No. MQTT 5 replaces them with reason codes such as 0x86 and 0x87, which carry more detail. The six 3.1.1 codes map directly to MQTT 5 equivalents.

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.