MQTT 错误码速查:含义与排查方法
遇到连接失败、订阅被拒绝或反复断线?先确认错误来自 Broker、客户端库还是网络,再按错误码查含义和排查步骤。
- 作者:
- Mqttable Editorial
- 发布:
先找到你的错误
不用从头读完。先确认客户端库或 MQTT 版本,再跳到对应代码。135 与 0x87 是同一个值;PubSubClient 的 -4 与 MQTT 3.1.1 CONNACK 的 4 不是。
- ESP32 / ESP8266(PubSubClient): rc=-2:连接失败;rc=-4:超时。
- 认证或权限被拒绝: 134 / 0x86:凭证错误;135 / 0x87:未授权。
- 连接反复断开: 141 / 0x8D:心跳超时;142 / 0x8E:会话被接管。
- Paho 报错: Python 7;Java 32109 / EOFException。
- 网络或证书错误: ECONNREFUSED、TLS、WebSocket 1006。
已经知道数值时,可直接用浏览器的页内查找。其他代码可从目录进入 MQTT 5、MQTT 3.1.1 或 客户端库。本文保留 77 个查码条目,包含 MQTT 5 全部 43 个已定义值;客户端库部分并非所有 SDK 错误的完整清单。
先看报文,再判断哪一步失败
CONNACK
- 它回答的问题
- Broker 是否接受连接请求?
SUBACK / UNSUBACK
- 它回答的问题
- 哪一项订阅或取消订阅被接受?按请求中的 Topic Filter 顺序对应。
PUBACK / PUBREC / PUBREL / PUBCOMP
- 它回答的问题
- 发布进行到哪一步 QoS 确认?
DISCONNECT
- 它回答的问题
- 为什么关闭连接?还需看谁发出了这个报文。
AUTH
- 它回答的问题
- 认证进行到哪一步?不一定是失败。
| 报文 | 它回答的问题 |
|---|---|
| CONNACK | Broker 是否接受连接请求? |
| SUBACK / UNSUBACK | 哪一项订阅或取消订阅被接受?按请求中的 Topic Filter 顺序对应。 |
| PUBACK / PUBREC / PUBREL / PUBCOMP | 发布进行到哪一步 QoS 确认? |
| DISCONNECT | 为什么关闭连接?还需看谁发出了这个报文。 |
| AUTH | 认证进行到哪一步?不一定是失败。 |
发布或订阅确认失败,不等于连接一定断开。同一个数字,放到不同报文或协议版本里,含义也可能改变。MQTT 5.0 · MQTT 3.1.1
MQTT 5 原因码
先按问题类型定位,再查看具体数值。每个条目都保留适用报文;SUBACK 或 PUBACK 失败,不等于连接已经断开。完整范围为 35 个失败码和 8 个成功或过程码。 MQTT 5.0 标准
身份与权限
132(0x84):不支持的协议版本
Unsupported Protocol Version · 报文: CONNACK
客户端申请的 MQTT 版本不被此端点接受。例如客户端选了 5.0,但这个 Broker 端点只支持 3.1.1。
- 编辑客户端的 MQTT version,选择服务端支持的版本后重连,在 Trace 检查 CONNACK。
- 版本是客户端设置,不是修改 TCP 端口就能解决。
133(0x85):Client ID 无效
Client Identifier not valid · 报文: CONNACK
Broker 拒绝这个 Client ID,例如格式或长度不符合要求。这与相同 ID 把旧连接挤下线的 0x8E 不同。
- 编辑 Client ID,使用服务端允许的标识,再在 Trace 验证 CONNACK 成功。
- 平台要求绑定设备 ID 时必须使用已授权的值,不能随意生成一个替代。
134(0x86):用户名或密码错误
Bad User Name or Password · 报文: CONNACK
连接认证未通过。例如保存的密码已被更换,或用户名、密码字段没有按平台要求填写。
- 编辑客户端认证信息,重新录入有效凭证,再看 Trace 中的 CONNACK。
- 先核对输入与凭证有效性;不要通过关闭证书验证“修复”MQTT 用户名或密码问题。
135(0x87):未授权
Not authorized · 报文: CONNACK、PUBACK、PUBREC、SUBACK、UNSUBACK、DISCONNECT
当前操作未获授权。例如能连接 Broker,但没有权限发布 factory/cmd。是否断线要看承载此码的报文,失败 PUBACK 不等于 DISCONNECT。
- 在 Trace 确认被拒绝的是连接、发布还是订阅;记录对应主题和身份。
- 让管理员检查该操作的权限,再用同一身份、同一主题重试,不要只换密码。
138(0x8A):客户端被封禁
Banned · 报文: CONNACK
服务端明确禁止该客户端连接。这不是普通的密码输入错误,反复重连也不会自动解除封禁。
- 在 Trace 保存拒绝结果,提供 Client ID、账号和发生时间给管理员核对封禁记录。
- 等待管理员处理后再重试;不要通过换身份绕过封禁。
140(0x8C):认证方法错误
Bad authentication method · 报文: CONNACK、DISCONNECT
认证方法不匹配或不被支持,例如双方对增强认证使用的方法不一致。它与“方法正确但密码错误”的 0x86 不同。
- 编辑客户端的 Authentication Method,选择 Broker 实际配置的方法,再检查 Trace 中的 CONNECT、AUTH 和 CONNACK。
- 方法名称与后续交互都必须匹配。
服务状态与地址迁移
136(0x88):服务不可用
Server unavailable · 报文: CONNACK
服务端响应了 MQTT 连接请求,但目前不能提供服务。与 TCP 根本连不上不同,这里已经收到了 CONNACK。
- 保存 Trace 中的 CONNACK 与时间戳,确认连的是正确环境,再核对 Broker 状态。
- 服务恢复后用同一连接配置重试。
137(0x89):服务端繁忙
Server busy · 报文: CONNACK、DISCONNECT
Broker 目前无法继续处理此客户端的请求;可能在建连时拒绝,也可能关闭已有连接。此码本身不能确定是 CPU、内存还是其他瓶颈。
- 先在 Mqttable 停止批量测试或密集发送;保存失败时间,让管理员核对 Broker 资源与日志。
- 恢复后从单个客户端开始验证,再逐步增加负载。
139(0x8B):服务端正在关闭
Server shutting down · 报文: DISCONNECT
服务端准备停止运行,因此结束已有连接。常见需要核对的事件是维护、重启或节点下线。
- 在 Trace 对齐 DISCONNECT 时间与 Broker 的维护记录。
- 等节点恢复或切换到管理员提供的可用端点,再验证连接和订阅;不用因此重置密码或 Client ID。
156(0x9C):临时切换服务器
Use another server · 报文: CONNACK、DISCONNECT
服务端要求客户端暂时换一个服务器,不是永久迁移通知。可选的 Server Reference 属性可能提供目标地址。
- 在 Trace 查看 Server Reference;先向管理员确认目标,再在 Connections 新建或切换 Broker 端点。
- 保留原地址,核对新端点的 TLS 与认证,不要假定 Mqttable 会自动重定向。
157(0x9D):服务器已迁移
Server moved · 报文: CONNACK、DISCONNECT
服务端要求客户端永久改用新的服务器位置。与 0x9C 的临时切换不同,后续配置也需要更新。
- 在 Trace 查看 Server Reference,或向管理员取得已确认的新地址。
- 更新 Connections 中 Broker 的 Host、Port 和必要的 TLS 设置,重连后验证订阅和消息接收。
心跳、会话与断开
141(0x8D):心跳超时
Keep Alive timeout · 报文: DISCONNECT
有效 Keep Alive 非零时,Broker 在其 1.5 倍时间内没有收到客户端的 MQTT 控制报文。例如有效值为 20 秒,对应 30 秒;不是只看有没有业务消息。
- 在客户端设置检查 Keep Alive,再看 CONNACK 是否返回 Server Keep Alive。
- 开启 Trace → Heartbeat 显示,检查 PINGREQ/PINGRESP;若客户端发不出心跳,排查休眠或线程阻塞,别直接设为 0。
142(0x8E):会话被同名客户端接管
Session taken over · 报文: DISCONNECT
另一个连接用了同一个 Client ID,Broker 因此关闭旧连接。例如调试工具误用了设备的 Client ID,双方自动重连后可能反复互相挤下线。
- 在 Connections 将调试客户端的 Client ID 改为获准的独立值,停掉重复实例后重连。
- 验证两个不同 ID 都能保持在线;切换 Clean Start 不能解决同时在线的 ID 冲突。
152(0x98):管理操作断开连接
Administrative action · 报文: DISCONNECT
连接因管理操作被关闭,例如管理员踢线或管理程序主动结束连接。可能由客户端或服务端发送,不能只看码值认定是 Broker 管理员操作。
- 在 Trace 先看 DISCONNECT 方向与时间戳,再核对对应一侧的管理日志。
- 确认操作已结束后重连;若持续发生,应修正管理策略,而不是加快自动重连。
160(0xA0):达到最长连接时限
Maximum connect time · 报文: DISCONNECT
服务端允许这条连接存在的时间已用完,即使消息和心跳一直正常也可能断开。它不是 Keep Alive 超时,也不是离线会话保留时间。
- 在 Trace 对照连接成功与 DISCONNECT 的时间,检查多次是否接近同一时长。
- 核对服务端连接时长策略及凭证生命周期;按要求更新认证材料或重新连接,而不是盲目调大 Keep Alive。
主题、订阅与功能支持
143(0x8F):主题过滤器无效
Topic Filter invalid · 报文: SUBACK、UNSUBACK、DISCONNECT
对方不接受这个 Topic Filter。标准也允许用它表示“语法正确,但此客户端不能使用”的过滤器,不能一律理解为通配符拼错。
- 在 Trace 对照 SUBSCRIBE/UNSUBSCRIBE 的过滤器,先用一个获准的具体主题替代宽泛过滤器重试。
- 另查
+、#的位置;语法畸形也可能报0x81或0x82。
144(0x90):主题名无效
Topic Name invalid · 报文: CONNACK、PUBACK、PUBREC、DISCONNECT
对方不接受这个 Topic Name。发布时检查 PUBLISH Topic;建连时被拒绝,还要检查 CONNECT 中的 Will Topic。它不只表示主题字符串编码错误。
- 在 Composer 将主题改为平台允许的具体路径,再发送一次;若错误在 CONNACK,修改客户端的遗嘱主题。
- 对照前后报文,确认改变的是 Topic Name,而非订阅过滤器。
148(0x94):主题别名无效
Topic Alias invalid · 报文: DISCONNECT
PUBLISH 使用了不被允许的 Topic Alias,例如对方只允许最大值 2,却发送别名 3。报文中显式携带别名 0 也不合法。
- 在 Trace 检查 PUBLISH 的 Topic Alias 与对方声明的 Topic Alias Maximum。
- 在发送设置中取消别名,改用完整主题重试;连接表单的 Topic Alias Max 控制本客户端接收方向。
154(0x9A):不支持保留消息
Retain not supported · 报文: CONNACK、DISCONNECT
客户端请求使用 Retain,但 Broker 不提供这项能力。既可能是普通发布开启了 Retain,也可能是连接时配置了 Will Retain。
- 在 Trace 检查 CONNACK 的 Retain Available;关闭 Composer → Retain 后重试。
- 若拒绝发生在建连阶段,关闭客户端的 Will Retain,不要只改发送窗口。
155(0x9B):不支持的 QoS
QoS not supported · 报文: CONNACK、DISCONNECT
例如 Broker 只支持到 QoS 1,客户端却发布 QoS 2,或配置了不被支持的 Will QoS。它不同于 SUBACK 成功时获准较低 QoS。
- 在 Trace 检查 CONNACK 的 Maximum QoS;在 Composer 选择不超过上限的 QoS 后重发。
- 若建连失败,检查并降低 Will QoS,同时确认较低等级符合业务要求。
158(0x9E):不支持共享订阅
Shared Subscriptions not supported · 报文: SUBACK、DISCONNECT
客户端使用了共享订阅,例如 $share/workers/lab/temp,但 Broker 不支持这项能力。连接成功不代表共享订阅也可用。
- 在 Trace 检查 CONNACK 的 Shared Subscription Available。
- 在订阅表单改为
lab/temp验证普通订阅;这会失去共享组分摊消息的语义,不能直接当作生产等价替代。
161(0xA1):不支持订阅标识符
Subscription Identifiers not supported · 报文: SUBACK、DISCONNECT
SUBSCRIBE 携带了 Subscription Identifier,但服务端不支持。这个数字用于标记订阅,不是 Client ID,也不是报文编号。
- 在订阅表单清空可选的 Subscription Identifier,不要填 0 代替省略,然后重试。
- 用 Trace 检查新 SUBSCRIBE 不再携带该属性,并确认 SUBACK 成功。
162(0xA2):不支持通配符订阅
Wildcard Subscriptions not supported · 报文: SUBACK、DISCONNECT
使用了带 + 或 # 的过滤器,例如 lab/+/temp 或 lab/#,但服务端不支持通配符订阅。
- 在订阅表单改填具体主题,例如
lab/device01/temp,再检查 Trace 中的 SUBACK。 - 需要多个主题时逐条订阅;同时检查 CONNACK 的 Wildcard Subscription Available。
在途消息、大小与配额
145(0x91):报文编号已被占用
Packet Identifier in use · 报文: PUBACK、PUBREC、SUBACK、UNSUBACK
报文编号(Packet Identifier)仍被另一项未完成的操作占用,却又分配给了新操作。它不是 Client ID,也不是 Payload 里的业务流水号;合法重传不应直接算作此错误。
- 在 Trace 或 PCAP 中按同一会话、同一发送方向核对编号与 ACK 链。
- 修复编号分配或会话状态;只有确认可以丢弃旧订阅和待处理状态时,才用 Clean Start 重建会话。
146(0x92):报文编号不存在
Packet Identifier not found · 报文: PUBREL、PUBCOMP
接收方找不到这个报文编号对应的 QoS 2 状态。恢复过程中可能出现,不能仅凭此码认定连接故障或业务消息丢失。
- 在 Trace 或 PCAP 中检查同一会话的 PUBLISH → PUBREC → PUBREL → PUBCOMP,结合重连时的 Session Present 判断状态是否延续;不要直接重新发布,避免业务重复。
147(0x93):超过接收并发上限
Receive Maximum exceeded · 报文: DISCONNECT
同时在途、尚未完成规定确认的 QoS 1/2 发布超过对方声明的 Receive Maximum。它是并发窗口限制,不是每秒消息数,也不是 Payload 大小限制。
- 在 Trace 对照 CONNECT/CONNACK 的 Receive Maximum 与 ACK 链,降低发送端的在途并发。
- 客户端表单的 Receive Max 声明的是自己的接收能力,调大它不会提高 Broker 的接收上限。
149(0x95):报文过大
Packet too large · 报文: CONNACK、DISCONNECT
接收方无法接受这么大的 MQTT 报文。上限包含主题、属性和协议头,不只是 Payload;例如接收上限为 1,024 字节时,2,048 字节 Payload 已经必然超限。
- 在 Trace 查看对方声明的 Maximum Packet Size;在 Composer 保持主题和 QoS 不变,先改发
{"ok":1}。 - 小消息通过后逐步缩减原消息;本地拒绝发送不等于 Broker 已返回此码。
150(0x96):消息速率过高
Message rate too high · 报文: DISCONNECT
接收方认为消息或数据到达速率过高。例如定时发送间隔太短,或者多个发布客户端叠加超过可接受速率。与 0x93 的在途数量限制不同。
- 先停止 Scheduled Messages 或 Bench,用 Composer 手动发送单条消息验证。
- 恢复正常后增加发送间隔、减少同时发送的客户端;按 Broker 实际限速配置确定目标速率。
151(0x97):配额用尽
Quota exceeded · 报文: CONNACK、PUBACK、PUBREC、SUBACK、DISCONNECT
某项实现或管理配额已用尽,例如订阅数量或消息相关配额。具体是哪一项,不能只靠这个码判断。
- 在 Trace 记录是 CONNECT、PUBLISH 还是 SUBSCRIBE 被拒绝,查看 Reason String。
- 依据 Broker 日志清理不需要的测试资源或调整配额,再重试同一操作。
159(0x9F):连接频率超限
Connection rate exceeded · 报文: CONNACK、DISCONNECT
单位时间内尝试建立的连接太多,例如大量设备同时重连。它不是“在线连接总数达到上限”,也不是发布消息太快。
- 停止 Mqttable 的批量建连测试,并检查原客户端是否循环重连。
- 先只连接一个客户端验证;实际应用使用逐步退避并加入随机间隔,随后按 Broker 的建连限速恢复。
报文格式与其他错误
128(0x80):未指定错误
Unspecified error · 报文: CONNACK、PUBACK、PUBREC、SUBACK、UNSUBACK、DISCONNECT
连接、发布或订阅被拒绝,但对方没有提供更具体的原因。不能仅凭此码判断密码错误或 Broker 过载。
- 在 Trace 打开失败报文,查看是否带有 Reason String,记录前一个请求与时间戳。
- 将同一操作缩减为最小请求重试,并核对 Broker 日志。
129(0x81):报文格式错误
Malformed Packet · 报文: CONNACK、DISCONNECT
收到的 MQTT 报文无法按协议正确解析,例如长度编码错误、固定头标志不合法或字符串编码损坏。与“内容是错误的 JSON”不是同一层问题。
- 将原设备的可解码抓包导入 PCAP,查看被拒绝前的报文;再用 Connections 执行同类正常操作作对照。
- 修复或升级有问题的编解码实现,而不是修改重连间隔。
130(0x82):协议错误
Protocol Error · 报文: CONNACK、DISCONNECT
通信流程违反协议,例如在已经完成连接的同一条网络连接上再次发送 CONNECT。报文字节能解析,也仍然可能违反协议状态。
- 在 Trace 或 PCAP 按同一连接检查报文顺序,定位失败前的请求。
- 停掉重复建连逻辑,用一个客户端实例重新测试;自研客户端应修正状态机。
131(0x83):实现相关错误
Implementation specific error · 报文: CONNACK、PUBACK、PUBREC、SUBACK、UNSUBACK、DISCONNECT
请求在协议上合法,但被对方的具体实现拒绝。准确原因需要实现方的说明,不能直接当作某一种固定配置错误。
- 在 Trace 记录 Reason String 和请求属性,先移除非必要属性做一次对照。
- 将可重复的最小请求与 Broker 版本交给服务端维护者检查。
153(0x99):载荷格式无效
Payload format invalid · 报文: CONNACK、PUBACK、PUBREC、DISCONNECT
例如把 Payload Format Indicator 声明为 UTF-8 文本,却发送无效的 UTF-8 字节。它不是通用的 JSON 语法错误码;CONNACK 中出现时还应检查 Will Payload。
- 在发送窗口核对 Payload Format Indicator 与真实字节。
- 文本修正为有效 UTF-8;二进制消息不要声明为 UTF-8。在 Trace 查看新响应;若是建连失败,修正遗嘱 Payload 后重连。
成功与认证过程:这些值不代表失败
以下 8 个不同数值不属于 0x80 及以上的失败码。0x00 有三种按报文区分的名称,在同一个条目中解释。MQTT 5.0 §2.4
0(0x00):成功、正常断开或获准 QoS 0
Success / Normal disconnection / Granted QoS 0 · 报文: CONNACK、PUBACK、PUBREC、PUBREL、PUBCOMP、UNSUBACK、AUTH、DISCONNECT、SUBACK
不是错误。CONNACK 中表示连接成功;DISCONNECT 中表示正常关闭;SUBACK 中表示获准 QoS 0;其他确认报文表示对应步骤成功。
- 在 Trace 先确认报文类型与发送方向。
- 若连接已断,查是谁发送 DISCONNECT;若只是发布确认成功,还需看到订阅方收到 PUBLISH 才能确认消息到达该客户端。
1(0x01):获准 QoS 1
Granted QoS 1 · 报文: SUBACK
该主题订阅已被接受,获准的最大 QoS 是 1;不是连接错误,也不表示所有收到的消息一定都是 QoS 1。
- 在 Trace 对照 SUBSCRIBE 和 SUBACK,确认是哪条 Topic Filter 获准。
- 继续观察接收的 PUBLISH;无需为这个原因码重连。
2(0x02):获准 QoS 2
Granted QoS 2 · 报文: SUBACK
该主题订阅已被接受,获准的最大 QoS 是 2。它不意味着业务数据库已经“只写入一次”。
- 在 Trace 检查 SUBACK,并查看实际 PUBLISH 的 QoS。
- 需要验证业务去重时,还要检查消费者的处理结果,而不只是 MQTT 确认报文。
4(0x04):断开并发布遗嘱
Disconnect with Will Message · 报文: DISCONNECT
客户端主动结束连接,并要求 Broker 按遗嘱规则发布此前配置的 Will Message。这不是用户名或密码错误,也不保证遗嘱立即发出。
- 在 Trace 确认是客户端发出的
0x04,再用独立订阅客户端观察 Will Topic。 - 检查原客户端的断开逻辑与 Will Delay;不要假定普通 Disconnect 按钮会发送此码。
16(0x10):没有匹配订阅者
No matching subscribers · 报文: PUBACK、PUBREC
Broker 接受了这次发布,但没有匹配订阅者。它不是发布失败;Broker 也可以选择返回 0x00 而不报告此情况。
- 在 Connections 用另一个测试 Client ID 订阅同一主题,等 SUBACK 成功后再发布。
- 在 Trace 检查订阅方的 PUBLISH,不要只等待原因码变化。
17(0x11):订阅不存在
No subscription existed · 报文: UNSUBACK
你取消的 Topic Filter 在当前会话里不存在。例如订阅的是 lab/#,取消时却填了 lab/temp。
- 在 Trace 对照之前的 SUBSCRIBE 与这次 UNSUBSCRIBE,使用原来的完整 Topic Filter;同时确认操作的是同一个客户端和会话。
- 通常不需要断开重连。
24(0x18):继续认证
Continue authentication · 报文: AUTH
增强认证正在进行多轮交互。收到此码本身不是失败,也不代表已经获得连接或发布权限。
- 在 Trace 观察后续 AUTH 与最终结果,核对客户端的 Authentication Method 是否与服务端一致。
- 若交互停住,检查原客户端是否实现了该认证方法的下一步。
25(0x19):重新认证
Re-authenticate · 报文: AUTH
已连接的客户端发起新一轮认证,使用原连接的认证方法。它不是服务端已经踢线的错误通知。
- 在 Trace 确认 AUTH 的发送方向,继续观察认证结果。
- 若之后掉线,查后续真正的失败码;不要把
0x19本身当成密码错误或自动重连要求。
MQTT 3.1.1 返回码
CONNACK 用 0x00 表示连接成功,0x01 至 0x05 表示连接被拒绝。SUBACK 中的 0x00、0x01、0x02 表示获准的 QoS,0x80 才表示订阅失败。MQTT 3.1.1 没有 MQTT 5 那样的服务端 DISCONNECT 原因码。MQTT 3.1.1 标准
0(0x00):连接成功;在订阅确认中表示 QoS 0
报文: CONNACK、SUBACK
不是错误。出现在 CONNACK 中表示连接被接受;出现在 SUBACK 中表示对应订阅获准使用 QoS 0。
- 在 Connections → Trace → Grid 打开该报文。
- 连接成功但收不到消息时,继续检查 SUBACK 和接收方的 PUBLISH,不要把建连成功当成消息已送达。
1(0x01):连接时版本不被接受;订阅时表示 QoS 1
报文: CONNACK、SUBACK
在 CONNACK 中表示 Broker 不接受此次 MQTT 协议版本;但在 SUBACK 中是订阅成功、获准 QoS 1。
- 先在 Trace 确认报文类型。
- 若是 CONNACK,编辑客户端的 MQTT version,改为该 Broker 支持的版本后重连;若是 SUBACK,不需要修复。
2(0x02):连接时 Client ID 被拒绝;订阅时表示 QoS 2
报文: CONNACK、SUBACK
在 CONNACK 中表示 Client ID 不符合服务端要求,例如长度或允许的字符不符合要求;在 SUBACK 中表示获准 QoS 2。
- 若是连接拒绝,在客户端表单修改 Client ID,使用服务端允许的非空标识后重连。
- 调试账号允许自选 ID 时,可用
mqttablecheck01;不要冒用线上设备 ID。
3(0x03):网络已连接,但 MQTT 服务不可用
报文: CONNACK
Broker 收到了连接请求,却暂时无法提供 MQTT 服务。不是单凭这个码就能认定 Wi-Fi 或网线故障。
- 在 Trace 保存 CONNACK 和时间戳,核对 Broker 的运行状态与日志。
- 确认服务恢复后再连接,避免连续点击重连增加压力。
4(0x04):用户名或密码被拒绝
报文: CONNACK
Broker 不接受本次提交的用户名或密码。常见检查点是输入错误、旧密码或凭证格式。
- 编辑客户端的认证信息,重新录入正确凭证并重连;在 Trace 检查新的 CONNACK。
- 不要为排查此码而关闭 TLS 证书校验,也不要把密码放进截图。
5(0x05):这个身份没有连接权限
报文: CONNACK
服务端不允许该客户端连接。即使用户名和密码正确,账号、设备或访问策略也可能不允许连接。
- 在客户端表单核对 Username、Client ID 和目标 Broker,把拒绝时间提供给管理员检查连接权限。
- 权限修正后重连,以 CONNACK
0x00验证。
128(0x80):订阅失败
报文: SUBACK
MQTT 3.1.1 的通用订阅失败码,不会具体说明是权限、过滤器限制还是其他拒绝原因。它不是 MQTT 5 的 Unspecified error。
- 在 Trace 将 SUBACK 各返回值按顺序对应到 SUBSCRIBE 的 Topic Filter;用一个获准的具体主题单独重试,再让管理员核对订阅权限。
客户端库错误码
这里的数值由客户端库定义。先核对库名称和版本,不要把同一个数字直接套进协议原因码表。
PubSubClient:ESP32 / ESP8266
这些值来自 PubSubClient 的 client.state(),Arduino 日志可能打印成 rc=-2 或 rc=-4。它们不是 MQTT 5 的“负数原因码”,其他 Arduino 库也不一定使用同样定义。正数 1 至 5 对应前面 MQTT 3.1.1 的连接返回含义。源码定义 连接实现
PubSubClient -2:底层连接未建立
代码 / 日志: MQTT_CONNECT_FAILED / rc=-2
底层 Arduino Client 没能建立连接。先检查地址、监听端口、网络路径及所用的 TLS 包装层;不能直接判断为 MQTT 密码错误。
- 在 Mqttable Connections 用独立的获准 Client ID,对照同一 Host、Port 和传输方式。
- 桌面端能连上后,再检查 ESP32/ESP8266 本身的网络;桌面连通不代表设备也能连通。
PubSubClient -4:等待响应超时
代码 / 日志: MQTT_CONNECTION_TIMEOUT / rc=-4
客户端等待响应超时。建连时可能是 CONNECT 发出后没有及时收到 Broker 响应;它不是 CONNACK 返回码 4。
- 用经授权的 Proxy 流量或设备的可解码 PCAP,对照 CONNECT/CONNACK 和心跳。
- 核对端点,并检查程序是否及时处理网络循环;先找到缺失的响应,不要只调大超时。
PubSubClient -3:原有连接丢失
代码 / 日志: MQTT_CONNECTION_LOST / rc=-3
已经建立的连接丢失了。这个状态没有直接说明是 Broker、设备还是网络导致中断。
- 保留设备断开前的日志,用 PCAP 检查 MQTT 结束报文或 TCP 关闭。
- 可以排查 Client ID 重复,但必须找证据,不能把所有连接丢失都判成会话抢占。
PubSubClient -1:当前未连接
代码 / 日志: MQTT_DISCONNECTED / rc=-1
客户端目前没有连接。它可能是初始状态或主动断开后的状态,本身不是前一次失败的具体原因。
- 在设备执行连接后立即记录返回值和状态,用 Mqttable Connections 做对照。
- 若对照成功,检查原程序的连接生命周期,不要寻找 Broker 发出的 -1 原因码。
Paho Python
下面明确指 paho.mqtt.enums.MQTTErrorCode,不要与 ConnackCode 或 ReasonCode 混用。Paho 的 VERSION2 回调也会为 MQTT 3 连接使用 ReasonCode,部分非零数值会改变;只在回调里看到 135,不能据此断定网络使用 MQTT 5。枚举 · 回调迁移
Paho Python 4:调用操作时客户端未连接
代码 / 日志: MQTT_ERR_NO_CONN
在 MQTTErrorCode API 枚举中,4 表示没有连接;不是 CONNACK 4 所表示的用户名或密码被拒绝。
- 依赖连接的操作应等待建连成功回调,并处理后续断线。
- 用 Mqttable Connections → Composer 对照发送一条消息;对照成功后检查 Python 程序的调用时机和连接状态。
Paho Python 5:客户端报告连接被拒绝
代码 / 日志: MQTT_ERR_CONN_REFUSED
MQTTErrorCode 枚举的 5 表示连接被拒绝。先确认这个数字来自该枚举、旧版 CONNACK 回调,还是 ReasonCode 对象。
- 保留 Python 回调名称和完整错误,用 Mqttable Trace 对照 CONNACK。
- 只有实际响应支持时才判为授权问题,不要把所有 API 错误 5 都归为密码错误。
Paho Python 7:连接丢失
代码 / 日志: MQTT_ERR_CONN_LOST
MQTTErrorCode 7 表示连接丢失。不要到 MQTT 5 原因码表寻找 0x07 来解释这个 API 错误。
- 记录 Python 日志、断开时间与最后一次通信,用经授权的 Proxy 或可解码 PCAP 区分 Broker 响应和传输中断;同时检查原客户端网络循环与服务端日志。
Paho Python 16:客户端检测到心跳超时
代码 / 日志: MQTT_ERR_KEEPALIVE
MQTTErrorCode 16 表示心跳失败;MQTT 5 PUBACK/PUBREC 的原因码 16(0x10)却表示没有匹配订阅者。
- 检查原客户端流量中的 PINGREQ/PINGRESP,确认网络循环及时运行;Mqttable 对照时开启 Trace → Heartbeat。
- 区分客户端发现的超时与 Broker 发出的 141 / 0x8D。
Paho Java
以下对应 org.eclipse.paho.client.mqttv3.MqttException。记录 getReasonCode() 时同时保留 getCause();这些五位数来自 Java 库,不是 MQTT 报文中的单字节原因码。异常定义 · 数值表 连接丢失问题记录
Paho Java 32000:等待服务端响应超时
代码 / 日志: REASON_CODE_CLIENT_TIMEOUT
Java 客户端等待服务端响应超时,其中包括等待心跳响应的情况。
- 保存异常,确认正在等待哪一个请求的响应。
- 在可解码 PCAP 或经授权的 Proxy 流量中找对应回复,区分网络无响应和程序无法及时处理入站数据。
Paho Java 32100:已经连接,却再次请求连接
代码 / 日志: REASON_CODE_CLIENT_CONNECTED
客户端已经连接。这是本地连接生命周期问题,不是 Broker 返回的拒绝码。
- 去掉重复 connect 调用,让一个组件统一负责重连。
- 用 Mqttable 的单连接作为对照;修改 Broker 凭证不能解决程序重复建连。
Paho Java 32103:无法连接服务器
代码 / 日志: REASON_CODE_SERVER_CONNECT_ERROR
Java 客户端没有成功连接服务器;更具体的原因需要继续看嵌套异常。
- 记录
getReasonCode()与getCause(),在 Mqttable Connections 对照同一端点与传输方式。 - 再根据嵌套异常检查原运行环境的 DNS、路由与 TLS 配置。
Paho Java 32104:未连接就执行了依赖连接的操作
代码 / 日志: REASON_CODE_CLIENT_NOT_CONNECTED
本次操作要求客户端处于连接状态,也可能是之前建立的连接已经丢失。
- 发布或订阅前,等待异步连接真正成功。
- 用 Mqttable 完成一次成功发布作为对照,再修正 Java 状态切换;调用 connect 不等于异步建连已经完成。
Paho Java 32109:Connection lost / java.io.EOFException
代码 / 日志: REASON_CODE_CONNECTION_LOST
连接意外结束。实际问题报告中有 Connection lost (32109) - java.io.EOFException 这样的写法;嵌套异常提供线索,但不能直接确定唯一根因。
- 保留完整异常链及同一时间的 Broker 日志。
- 在 PCAP 中看最后一组 MQTT 报文与连接关闭,再依据证据排查重复 ID、服务端重启及网络路径。
Paho Java 32110:上一次连接还没结束,又开始连接
代码 / 日志: REASON_CODE_CONNECT_IN_PROGRESS
同一个客户端已有一次连接尝试正在进行。
- 串行执行连接尝试,等待成功或失败后再决定下一步。
- 避免重试定时器与自动重连各自发起连接;Mqttable 仅用于单连接对照,原程序的状态机仍需修正。
Paho Java 32202:本地在途消息窗口已满
代码 / 日志: REASON_CODE_MAX_INFLIGHT
Java 客户端自己的在途消息数量达到限制,不等于 Broker 已返回 147 / 0x93。
- 降低并发发布数量,通过 PCAP 或 Proxy 检查 ACK 是否完成。
- 用 Mqttable 单条消息验证基本投递;先排查缺失 ACK,再决定是否调整本地在途上限。
Qt / C-more 设备
Qt MQTT 定义了以下四个客户端错误,AutomationDirect C-more 设备文档也列出了这些值。它们大于 0xFF,不可能装进 MQTT 5 的单字节 Reason Code 字段。其他客户端未必使用同样的数字,Mqttable 也不一定显示这些编号。参考码表 · 客户端错误定义
Qt 256(0x100):底层连接出错
来源: 客户端状态,不是 MQTT 报文原因码
客户端报告底层传输失败或连接意外中断。它不是 Broker 发来的 MQTT 原因码。
- 用独立测试 Client ID 在 Connections 配置相同地址、端口和传输方式,检查连接错误;原设备的问题需结合其日志或导入 PCAP,区分 TCP 中断与 TLS 握手失败。
Qt 257(0x101):客户端发现协议违规并关闭连接
来源: 客户端状态,不是 MQTT 报文原因码
客户端认为收到的数据或协议流程不合法,因此主动断开。不要把它当成 MQTT 5 的 0x81。
- 保存原客户端日志和故障前报文;在 PCAP 中查看可解码的 MQTT 流,再用 Mqttable 的正常发布/订阅作为对照,缩小到原客户端、服务端或中间代理。
Qt 258(0x102):客户端无法归类的错误
来源: 客户端状态,不是 MQTT 报文原因码
只有“未知错误”,没有足够信息判断是网络、认证还是实现问题。此码不能直接给出唯一修复方法。
- 用 Connections 对照连接同一 Broker,记录两边结果;收集原客户端版本、完整错误日志及时间戳。
- 若无法复现,保留原设备抓包,不要只留下这个数字。
Qt 259(0x103):需要继续查看真正的 MQTT 5 原因码
来源: 客户端状态,不是 MQTT 报文原因码
这是“错误属于 MQTT 5”的外层提示,不是最终原因;例如里面还可能有 0x87。
- 查看原客户端的详细错误属性;若在 Mqttable 复现,在 Trace 打开 CONNACK、ACK 或 DISCONNECT,取得真正的 Reason Code,再查 MQTT 5 对应条目。
ESP-IDF / ESP-TLS
使用 ESP32 时还要确认库名称:ESP-IDF 的传输层错误与 PubSubClient 状态属于不同体系。ESP-IDF 错误参考
ESP-IDF 32774(0x8006):ESP-TLS 底层建连超时
代码 / 日志: ESP_ERR_ESP_TLS_CONNECTION_TIMEOUT / 0x8006
ESP-IDF 在建立底层连接时超时。这个枚举值不是 MQTT CONNACK,也不能单凭名称断定证书有问题。
- 同时读取 ESP-IDF 错误类型和传输层详情,检查设备端点与网络,再在 Mqttable Connections 用相同传输方式对照。
- 同样使用 ESP32,也不能把它和 PubSubClient 的 rc=-4 混用。
网络、TLS 与 WebSocket 错误
前六项采用 Node.js 错误名称,可能出现在 MQTT 应用的底层日志里;WebSocket 1006 则属于另一层协议。先定位报错层级,不要一律修改 MQTT 密码或 QoS。Node.js 错误 · WebSocket 关闭码
TCP 与 DNS
ECONNREFUSED:底层连接被拒绝,MQTT 尚未成功建连
代码 / 日志: connect ECONNREFUSED
在 Node.js 错误体系中,目标主动拒绝底层连接。它与 MQTT CONNACK 中的授权拒绝不是一回事。
- 检查 Broker 是否监听、地址与端口是否正确,以及容器网络是否可达。
- 在 Mqttable Connections 用同一传输方式对照;桌面成功不能证明原程序容器内的 DNS 和路由正常。
ECONNRESET:连接被强制重置
代码 / 日志: read ECONNRESET / connection reset by peer
底层连接被强制关闭。这个错误并不能指出违反了哪一条 MQTT 规则。
- 对齐 Broker 和代理日志,用 PCAP 找到对应 TCP 流;未看到 MQTT 响应时,将传输失败与 MQTT 拒绝分开。
- 不能仅凭重置就判定密码错误。
ETIMEDOUT:连接或网络操作没有及时得到响应
代码 / 日志: connect ETIMEDOUT
网络操作没有在超时时间内完成。它不是 MQTT 5 的 Keep Alive 原因码。
- 先确认超时发生在 MQTT CONNECT 之前、建连过程中,还是已有连接上。
- 对照 Connections 错误与原设备 PCAP,优先检查网络路径,再决定是否调整计时器。
ENOTFOUND:Broker 主机名解析失败
代码 / 日志: getaddrinfo ENOTFOUND
客户端无法解析主机名。修改 MQTT 用户名、Topic 或 QoS 不能修复域名解析。
- 核对 Host 拼写,并在原运行环境检查 DNS;用 Mqttable Connections 对照同一主机名,留意内外网或容器 DNS 差异。
- 不要永久改成会破坏 TLS 主机名校验的 IP 地址。
TLS 证书校验
CERT_HAS_EXPIRED:证书已超过有效期
代码 / 日志: CERT_HAS_EXPIRED
证书验证发现证书超过有效期。这是 TLS 错误,不是 MQTT 原因码。
- 检查原设备时间与证书链,更新过期证书。
- Mqttable Connections 对照时保持证书验证开启;关闭验证不是生产修复方案。
ERR_TLS_CERT_ALTNAME_INVALID:连接地址与证书中的名称不匹配
代码 / 日志: ERR_TLS_CERT_ALTNAME_INVALID
访问的主机名或 IP 不在证书允许的名称范围内。
- 使用证书覆盖的正确 Broker 主机名,或签发匹配的证书。
- 在 Mqttable Connections 验证修正后的 Host 和 TLS 设置,不要跳过主机名验证来隐藏问题。
WebSocket
1006:未收到关闭帧就异常结束
代码 / 日志: WebSocket close code 1006
端点在没有收到 WebSocket Close 帧时报告异常关闭。1006 是保留值,不是对端在 Close 帧里发送的值,也不是 MQTT 原因码。
- 检查原浏览器的握手、WS/WSS 端点与反向代理日志。
- 在 Mqttable Connections 对照相同 WebSocket 端点,区分 WebSocket 失败和 MQTT 拒绝;原生客户端成功不能验证所有浏览器策略。
如何取证和验证修复
本文以本站开发的 MQTT 工作台 Mqttable 为操作示例;使用其他客户端时,也可以对照客户端日志与 Broker 日志完成这些检查。界面名称按 Mqttable 英文界面保留,便于查找。
用 Trace 找到原始响应
在 Mqttable 找原始响应: 打开 Connections → 对应 Broker → Trace → Grid,清空 Topic/Payload 过滤,选择客户端并打开响应,检查类型、方向和原因。排查心跳时开启 Heartbeat;被过滤的报文不能当作没有收到。Trace 操作
外部设备的问题需要设备日志、可解码的 PCAP,或经授权通过 Proxy 的流量;Connections 的 Trace 不会自动看到其他程序。对照测试使用同一端点和传输方式、独立的获准测试身份。桌面成功不代表原设备的 DNS、路由和 TLS 配置相同;这些操作是排查建议,不是逐码实测记录。
没有原因码时怎么查
没有 MQTT 原因码,不等于没有故障。先检查 Connections 的连接错误与最后几条 Trace,再结合原设备日志和 PCAP 排查 TCP 关闭、TLS 握手或网络中断。仅有加密的 TLS 抓包时,不能假定 Mqttable 能直接读出其中的 MQTT 原因码。PCAP 能力边界
修复后重试原来失败的那一个操作:连接错误看 CONNACK,订阅错误看 SUBACK,发布错误看对应确认与订阅方接收,断线错误看能否在原触发条件下保持连接。不要只凭界面的 Connected 就结束排查。发布与验证
求助前准备什么
- 客户端库及版本、MQTT 版本、完整错误原文。
- 失败的操作、报文类型与方向、时间戳,以及对应的 Broker 日志。
- 连接端点和传输方式,以及原设备或容器能否复现。
分享日志和抓包前,删去密码、令牌、私钥及敏感载荷。记录改了什么,并确认原来失败的操作现在是否成功。
协议数值以 MQTT 5.0 和 MQTT 3.1.1 标准为准,客户端与网络错误以各节链接的一手来源为准。文中操作是排查建议,不表示每个代码都已在 Mqttable 中复现。