Los códigos de motivo de MQTT (reason codes) son los valores de estado de un solo byte que los brokers y clientes MQTT 5 incluyen en los acuses de recibo y en los paquetes DISCONNECT para indicar exactamente qué ha ocurrido. Esta referencia recoge todos los códigos, qué paquetes pueden transportarlos, qué suele provocarlos y cómo resolverlos, para que puedas convertir un críptico 0x87 o 0x97 en tus registros en un siguiente paso concreto.
Cómo funcionan los códigos de motivo de MQTT
MQTT 3.1.1 solo tenía un puñado de valores de estado: seis códigos de retorno de CONNACK y un único valor de fallo (0x80) en SUBACK. Todo lo demás, como una publicación rechazada o una desconexión iniciada por el broker, era silencioso; la conexión simplemente se cerraba. MQTT 5 lo sustituyó por un conjunto compartido de reason codes que se usa en CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, UNSUBACK, DISCONNECT y AUTH. Es una de las funciones de MQTT 5 más prácticas para cualquiera que opere dispositivos en campo.
Dos reglas facilitan la lectura de la tabla:
- Los valores por debajo de
0x80indican éxito o un resultado que no es un error, como "QoS 1 concedido" o "no hay suscriptores coincidentes". - Los valores de
0x80en adelante indican fallo. El paquete que transporta el código te dice qué operación ha fallado.
Muchos paquetes también pueden llevar una propiedad Reason String, una explicación legible del emisor. Regístrala siempre que aparezca; a menudo contiene el límite exacto que se ha alcanzado.
Códigos de éxito e informativos
| Código | Nombre | Paquetes | Qué significa |
|---|---|---|---|
| 0x00 | Success / Normal disconnection / Granted QoS 0 | CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, UNSUBACK, AUTH, DISCONNECT, SUBACK | La operación tuvo éxito; en SUBACK significa que la suscripción se concedió con QoS 0 |
| 0x01 | Granted QoS 1 | SUBACK | Suscripción aceptada con QoS máximo 1 |
| 0x02 | Granted QoS 2 | SUBACK | Suscripción aceptada con QoS máximo 2 |
| 0x04 | Disconnect with Will Message | DISCONNECT | El cliente se desconecta limpiamente pero pide al broker que publique su Will |
| 0x10 | No matching subscribers | PUBACK, PUBREC | Publicación aceptada, pero nadie estaba suscrito |
| 0x11 | No subscription existed | UNSUBACK | Te diste de baja de un filtro al que no estabas suscrito |
| 0x18 | Continue authentication | AUTH | Continúa el intercambio de autenticación mejorada |
| 0x19 | Re-authenticate | AUTH | El cliente inicia una reautenticación en una conexión existente |
Un SUBACK que concede un QoS inferior al solicitado no es un error. Si pides QoS 2 y recibes 0x01, el QoS máximo del broker para tu conexión es 1, y los mensajes llegarán como mucho con QoS 1.
Referencia de códigos de error
| Código | Nombre | Paquetes | Causa habitual | Solución |
|---|---|---|---|---|
| 0x80 | Unspecified error | Todos los acuses de recibo, DISCONNECT | El broker rechazó la operación sin un motivo concreto | Lee la Reason String y los registros del broker |
| 0x81 | Malformed Packet | CONNACK, DISCONNECT | Codificación no válida, UTF-8 incorrecto, longitudes erróneas | Actualiza la librería cliente; revisa los codificadores propios |
| 0x82 | Protocol Error | CONNACK, DISCONNECT | Paquete válido pero enviado en el orden incorrecto o con campos prohibidos | Busca CONNECT duplicados o un mal uso de las propiedades |
| 0x83 | Implementation specific error | CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT | Una política propia del broker rechazó la operación | Consulta la documentación del broker y la Reason String |
| 0x84 | Unsupported Protocol Version | CONNACK | El broker no habla la versión solicitada | Usa MQTT 3.1.1 o 5.0 según lo que se admita |
| 0x85 | Client Identifier not valid | CONNACK | Client ID vacío, demasiado largo o no permitido | Usa un client ID válido y único |
| 0x86 | Bad User Name or Password | CONNACK | Credenciales incorrectas o mal formadas | Vuelve a copiar el usuario y la contraseña |
| 0x87 | Not authorized | CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT | Las credenciales o el cliente carecen de permiso | Revisa las credenciales, la vinculación del client ID y los permisos sobre topics |
| 0x88 | Server unavailable | CONNACK | El broker no puede aceptar conexiones en este momento | Reintenta con backoff |
| 0x89 | Server busy | CONNACK, DISCONNECT | El broker está sobrecargado | Reintenta más tarde con backoff y jitter |
| 0x8A | Banned | CONNACK | El cliente o la dirección están bloqueados | Detén los bucles de reintento; corrige las credenciales; contacta con el operador |
| 0x8B | Server shutting down | DISCONNECT | Apagado o reinicio planificado | Reconéctate con backoff |
| 0x8C | Bad authentication method | CONNACK, DISCONNECT | Método de autenticación mejorada no admitido | Elimina o corrige el Authentication Method |
| 0x8D | Keep Alive timeout | DISCONNECT | No se recibió ningún paquete en 1,5 x keep alive | Atiende el bucle de red; comprueba la conectividad |
| 0x8E | Session taken over | DISCONNECT | Otro cliente se conectó con el mismo client ID | Da a cada cliente un ID único |
| 0x8F | Topic Filter invalid | SUBACK, UNSUBACK, DISCONNECT | Filtro mal formado, p. ej. a/#/b |
Corrige la posición de los comodines |
| 0x90 | Topic Name invalid | CONNACK, PUBACK, PUBREC, DISCONNECT | Comodines o caracteres no válidos en un topic de publicación | Publica en un nombre de topic concreto |
| 0x91 | Packet Identifier in use | PUBACK, PUBREC, SUBACK, UNSUBACK | ID de paquete reutilizado mientras seguía en vuelo | Fallo de la librería o gestión propia de los ID de paquete |
| 0x92 | Packet Identifier not found | PUBREL, PUBCOMP | Estado de QoS 2 perdido, a menudo tras un reinicio de sesión | Revisa la configuración de persistencia de sesión |
| 0x93 | Receive Maximum exceeded | DISCONNECT | Demasiadas publicaciones QoS 1/2 sin confirmar | Respeta el Receive Maximum del otro extremo |
| 0x94 | Topic Alias invalid | DISCONNECT | Alias fuera de rango o no definido | Reinicia los alias al reconectar; respeta el máximo |
| 0x95 | Packet too large | CONNACK, DISCONNECT | El paquete supera el Maximum Packet Size | Reduce o trocea los payloads |
| 0x96 | Message rate too high | DISCONNECT | Publicación más rápida de lo permitido | Limita el ritmo de publicación |
| 0x97 | Quota exceeded | CONNACK, PUBACK, PUBREC, SUBACK, DISCONNECT | Se alcanzó un límite del plan o de la cuenta | Reduce el uso o mejora el plan |
| 0x98 | Administrative action | DISCONNECT | Un operador desconectó al cliente | Consúltalo con el administrador del broker |
| 0x99 | Payload format invalid | CONNACK, PUBACK, PUBREC, DISCONNECT | El Payload Format Indicator indica UTF-8 pero los bytes no lo son | Corrige la codificación o elimina el indicador |
| 0x9A | Retain not supported | CONNACK, DISCONNECT | Flag retain usado donde no está permitido | Publica sin retain |
| 0x9B | QoS not supported | CONNACK, DISCONNECT | QoS de publicación superior al máximo del broker | Baja el QoS al máximo anunciado |
| 0x9C | Use another server | CONNACK, DISCONNECT | El broker redirige al cliente temporalmente | Sigue la propiedad Server Reference |
| 0x9D | Server moved | CONNACK, DISCONNECT | El broker redirige de forma permanente | Actualiza el host configurado |
| 0x9E | Shared Subscriptions not supported | SUBACK, DISCONNECT | Filtro $share/... no permitido |
Usa un plan o un broker con suscripciones compartidas |
| 0x9F | Connection rate exceeded | CONNACK | Demasiados intentos de conexión | Añade backoff a la lógica de reconexión |
| 0xA0 | Maximum connect time | DISCONNECT | Se alcanzó el límite de duración de la conexión | Reconéctate; cuenta con desconexiones periódicas |
| 0xA1 | Subscription Identifiers not supported | SUBACK, DISCONNECT | Propiedad Subscription Identifier no permitida | Elimina la propiedad |
| 0xA2 | Wildcard Subscriptions not supported | SUBACK, DISCONNECT | Comodines no permitidos para este cliente | Suscríbete a topics explícitos |
Ten en cuenta que algunos códigos, como 0x8D y 0x8E, solo aparecen en un DISCONNECT enviado por el broker. Otros, como los de 0x84 a 0x88, solo pueden aparecer en CONNACK, porque describen por qué se rechazó la conexión desde el principio.
Equivalencia entre los códigos de retorno de MQTT 3.1.1 y MQTT 5
Si admites ambas versiones del protocolo, esta equivalencia mantiene coherente tu gestión de errores. La explicación detallada está en la guía de códigos de retorno CONNACK de MQTT.
| CONNACK de MQTT 3.1.1 | Equivalente en MQTT 5 |
|---|---|
| 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 |
Registrar los códigos de motivo MQTT en Python
Con paho-mqtt 2.x y CallbackAPIVersion.VERSION2, cada callback recibe un objeto ReasonCode. Su forma de cadena es el nombre de la especificación y .value es el código numérico, justo lo que quieres en los registros:
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()
Ejecútalo contra el servidor MQTT público gratuito de broker.connectmq.com (pensado para pruebas y prototipos, no para datos de producción) y verás Success al conectar y un QoS concedido en el SUBACK. Prueba a suscribirte a un filtro no válido como connectmq/demo/b7d2/#/x para ver 0x8F Topic Filter invalid en acción. Con MQTT.js, la misma información está en connack.reasonCode, en el array granted del callback de suscripción y en el paquete del evento disconnect.
Flujo de depuración según el código de motivo
Un orden práctico para investigar un fallo:
- Identifica el paquete. Un código en CONNACK es un problema de conexión; en SUBACK, un problema de suscripción; en DISCONNECT, un problema en tiempo de ejecución.
- Lee la Reason String. Normalmente reduce la causa a un límite o una regla concretos.
- Comprueba primero las credenciales y la identidad para
0x85,0x86y0x87. La guía para resolver MQTT connection refused: not authorized lo trata en profundidad. - Comprueba los límites para
0x93,0x95,0x96y0x97: tamaño de mensaje, ritmo, número de conexiones y mensajes en vuelo. - Comprueba la higiene de la flota para
0x8E: los client ID duplicados son la causa más habitual de los bucles de desconexión.
Códigos de motivo en ConnectMQ
ConnectMQ admite MQTT 3.1.1 y 5.0, así que los clientes MQTT 5 reciben los reason codes anteriores. Las cuotas del plan aparecen como 0x97 Quota exceeded, por ejemplo cuando un broker Serverless gratuito ya tiene su máximo de 25 conexiones. Las funciones del plan también importan: el plan gratuito concede QoS hasta 1, así que una suscripción con QoS 2 se concede con un QoS inferior en el SUBACK, mientras que QoS 2 y las suscripciones compartidas llegan con el plan Serverless de pago. Los inicios de sesión fallidos repetidos se tratan como sospechosos y la IP de origen puede bloquearse, así que nunca dejes un cliente reintentando con credenciales erróneas en un bucle cerrado.
Para saber más sobre qué hace un broker MQTT con estos códigos, o ver el protocolo completo en contexto, consulta el tutorial de MQTT.
Depura más rápido con ConnectMQ
Un broker MQTT gestionado con cliente en el navegador y explorador de topics en directo hace mucho más rápido reproducir una publicación o suscripción que falla. Crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito, y prueba tu gestión de errores con respuestas reales de MQTT 5.
Preguntas frecuentes
¿Qué diferencia hay entre un código de motivo y un código de retorno?
Código de retorno (return code) es el término de MQTT 3.1.1 para los seis valores de CONNACK. MQTT 5 generalizó la idea en códigos de motivo (reason codes) que aparecen en casi todos los acuses de recibo y en DISCONNECT.
¿Qué códigos de motivo indican éxito?
Cualquier valor por debajo de 0x80. Eso incluye 0x00 Success, 0x01 y 0x02 para el QoS concedido y valores informativos como 0x10 No matching subscribers.
¿Qué significa el código de motivo MQTT 0x97?
0x97 es Quota exceeded: el cliente alcanzó un límite, como el máximo de conexiones o una restricción del plan. Reduce el uso o pásate a un plan con límites más altos.
¿Reciben los clientes MQTT 3.1.1 los códigos de motivo de MQTT 5?
No. Una conexión 3.1.1 solo recibe los códigos de retorno clásicos de CONNACK y el valor de fallo 0x80 en SUBACK. Conéctate con la versión 5 del protocolo para recibir códigos de motivo detallados.



