Una conexión MQTT puede fallar en cuatro capas: la red (DNS y TCP), el transporte (TLS o WebSocket), el handshake de MQTT (CONNECT / CONNACK) y la sesión activa (keep-alive y desconexiones). Identificar la capa es media solución. Esta guía las recorre una a una, con los mensajes de error y reason codes exactos que probablemente verás.
Diagnóstico rápido
| Síntoma | Capa | Causa más probable |
|---|---|---|
Error ENOTFOUND / getaddrinfo | DNS | Errata en el hostname, o un esquema de URL incluido en el campo host |
| Se queda colgado y acaba en timeout | Red | Un firewall descarta el puerto, puerto incorrecto o puerto TCP usado desde un navegador |
ECONNREFUSED | Red | No hay nada escuchando en ese host y puerto |
| Error de certificado o de handshake | TLS | CA no confiable, hostname que no coincide o TLS contra un puerto sin cifrar |
| La conexión se cierra justo después de conectar | MQTT | CONNACK con un código de error: consulta las tablas de abajo |
| Se conecta y se cae una y otra vez | Sesión | Client ID duplicado o keep-alive mal configurado |
Timeouts de conexión
Un timeout significa que tus paquetes salieron y no volvió nada. El broker ni siquiera tuvo la oportunidad de decir que no. Comprueba, en este orden:
- Que el puerto corresponda al transporte.
mqtt://en 1883,mqtts://en 8883,wss://en el puerto WebSocket del broker. Consulta puertos MQTT explicados. - Que tu red permita el puerto. Las redes corporativas y de invitados suelen bloquear 1883 y 8883. Prueba
wss://en el 443 para confirmarlo. - Que el broker esté funcionando. Los sandboxes públicos se reinician sin previo aviso; prueba con otro broker para descartar tu propia configuración.
- Que el firewall de tu servidor esté abierto. En brokers autoalojados, el security group del cloud debe permitir tráfico entrante en el puerto del listener.
ECONNREFUSED
ECONNREFUSED es un rechazo rápido y explícito del sistema operativo: el host es accesible, pero no hay ningún proceso escuchando en ese puerto. Las causas típicas son un broker que no está en marcha, un listener enlazado solo a 127.0.0.1, un puerto de contenedor que nunca se publicó (docker run -p 1883:1883) o conectarse a localhost desde dentro de un contenedor cuando el broker vive en otro.
Errores de TLS
- CA autofirmada o desconocida (
SELF_SIGNED_CERT_IN_CHAIN,UNABLE_TO_VERIFY_LEAF_SIGNATURE): proporciona al cliente el archivo de la CA. No desactives la verificación fuera de las pruebas en local. - El hostname no coincide (
ERR_TLS_CERT_ALTNAME_INVALID): conéctate usando el nombre que figura en el certificado, no una dirección IP ni un alias interno. - Wrong version number / packet length too long: estás hablando TLS con un puerto sin cifrar (por ejemplo,
mqtts://en 1883) o MQTT sin cifrar con un puerto TLS. - Certificado caducado: comprueba las fechas con
openssl s_client -connect host:8883 -servername host.
Errores exclusivos del navegador: contenido mixto y rutas WebSocket
Los navegadores solo pueden usar MQTT sobre WebSockets, y añaden sus propias reglas. Una página servida por HTTPS no puede abrir ws://: el navegador lo bloquea como contenido mixto antes de enviar ningún paquete, y el error solo aparece en la consola de desarrollo. Usa wss://. El otro error frecuente es olvidar la ruta: muchos brokers esperan /mqtt y, sin ella, el upgrade a WebSocket falla con un 404. Más información en MQTT sobre WebSockets.
Prueba una conexión wss que funciona
Códigos de retorno del CONNACK en MQTT 3.1.1
Si la red y el transporte funcionan, el broker responde al CONNECT con un CONNACK. En MQTT 3.1.1 lleva un código de retorno de un byte:
| Código | Significado | Qué corregir |
|---|---|---|
0 | Connection accepted (conexión aceptada) | Nada: estás conectado |
1 | Unacceptable protocol version (versión de protocolo no aceptada) | El broker no soporta la versión solicitada; cambia entre MQTT 3.1.1 y 5 |
2 | Identifier rejected (identificador rechazado) | El client ID no es válido, es demasiado largo, está vacío cuando no se permite o está bloqueado |
3 | Server unavailable (servidor no disponible) | El broker está sobrecargado o en mantenimiento; reintenta con backoff |
4 | Bad user name or password (usuario o contraseña incorrectos) | Revisa las credenciales y su codificación |
5 | Not authorized (no autorizado) | La cuenta no tiene permiso para conectarse; revisa las ACL del broker o la regla del client ID |
Reason codes habituales de MQTT 5
MQTT 5 sustituye los códigos de retorno por reason codes. Todo lo que sea 0x80 o superior es un fallo. Los mismos códigos pueden aparecer en el CONNACK o en un DISCONNECT enviado por el servidor:
| Código | Nombre | Causa típica |
|---|---|---|
0x80 | Unspecified error | Fallo genérico; revisa los logs del broker |
0x84 | Unsupported Protocol Version | El broker solo habla 3.1.1 |
0x85 | Client Identifier not valid | Client ID mal formado o no permitido |
0x86 | Bad User Name or Password | Credenciales incorrectas |
0x87 | Not authorized | Usuario válido sin permiso para conectarse |
0x88 | Server unavailable | El broker no puede aceptar conexiones en este momento |
0x8A | Banned | Cliente o IP bloqueados por el operador |
0x8C | Bad authentication method | Método de autenticación mejorada no soportado |
0x95 | Packet too large | El CONNECT supera el tamaño máximo de paquete del broker |
0x97 | Quota exceeded | La cuenta ha alcanzado un límite de conexiones o de uso |
0x9F | Connection rate exceeded | Reconexiones demasiado rápidas; añade backoff |
Hay dos más que aparecen en paquetes DISCONNECT durante una sesión: 0x8D (Keep Alive timeout) y 0x8E (Session taken over). Ambos se explican más abajo. Para una visión general de lo que cambió en MQTT 5, consulta MQTT 5 vs 3.1.1.
Bucles por robo de client ID
La especificación obliga al broker a desconectar a un cliente existente cuando llega una nueva conexión con el mismo client ID. Si dos dispositivos —o dos pestañas del navegador— comparten ID y ambos se reconectan automáticamente, se van expulsando por turnos cada pocos segundos. En MQTT 5 el perdedor recibe un DISCONNECT con 0x8E; en 3.1.1 el socket simplemente se cierra.
Desconexiones por keep-alive
El valor de keep-alive del CONNECT indica al broker cada cuánto debe esperar tráfico. Si no ve nada durante una vez y media ese intervalo, cierra la conexión. Los clientes envían PINGREQ cuando están inactivos, pero un event loop bloqueado, un dispositivo dormido o un timeout de inactividad agresivo de un NAT o balanceador de carga pueden seguir rompiendo la conexión. Mantén el intervalo por debajo del timeout de inactividad de tu red (60 segundos es un valor por defecto habitual) y nunca bloquees el hilo que atiende al cliente MQTT.
Registra el error real
La mayoría de las librerías ocultan el motivo a menos que lo escuches explícitamente. Con MQTT.js:
import mqtt from 'mqtt';
const client = mqtt.connect('wss://broker.emqx.io:8084/mqtt', {
protocolVersion: 5,
clientId: `debug-${Math.random().toString(16).slice(2, 10)}`,
reconnectPeriod: 5000,
});
client.on('connect', (connack) => console.log('connected', connack.reasonCode ?? 0));
client.on('error', (err) => console.error('error:', err.message, err.code));
client.on('disconnect', (packet) => console.warn('server disconnect, reason', packet.reasonCode));
client.on('close', () => console.warn('socket closed'));Checklist
- Confirma el circuito completo con un broker público que funcione en el cliente online.
- Haz coincidir esquema, puerto y ruta con el listener documentado del broker.
- Lee el código del CONNACK o el reason code en lugar de adivinar.
- Usa un client ID único y reconecta con backoff.
- Mantén el keep-alive por debajo de cualquier timeout de inactividad del camino.
Para un procedimiento de prueba completo, sigue cómo probar un broker MQTT.
Preguntas frecuentes
¿Qué significa «connection refused: not authorized» en MQTT?
El broker aceptó la conexión de red pero rechazó al cliente. En MQTT 3.1.1 es el código de retorno 5 del CONNACK; en MQTT 5, el reason code 0x87. Las credenciales pueden ser válidas, pero la cuenta no tiene permiso para conectarse o para usar ese client ID.
¿Por qué mi cliente MQTT se desconecta y reconecta continuamente?
La causa más habitual es que dos clientes usan el mismo client ID. El broker desconecta la sesión más antigua cada vez que se conecta una nueva con ese ID y, si ambos se reconectan automáticamente, se expulsan mutuamente en bucle. Da a cada cliente un ID único.
¿Por qué mi conexión MQTT acaba en timeout en lugar de fallar?
Un timeout significa que no hubo ninguna respuesta, lo que suele indicar un firewall que descarta paquetes en silencio, un host incorrecto o el uso del puerto TCP desde un navegador. Un rechazo, en cambio, significa que algo respondió y dijo que no.