Uma conexão MQTT pode falhar em quatro camadas: a rede (DNS e TCP), o transporte (TLS ou WebSocket), o handshake MQTT (CONNECT / CONNACK) e a sessão ativa (keep-alive e desconexões). Identificar a camada já é metade da solução. Este guia percorre cada uma delas, com as mensagens de erro e os reason codes exatos que você provavelmente vai encontrar.
Triagem rápida
| Sintoma | Camada | Causa mais provável |
|---|---|---|
Erro ENOTFOUND / getaddrinfo | DNS | Erro de digitação no hostname, ou um esquema de URL incluído no campo de host |
| Fica travado e depois dá timeout | Rede | Firewall descartando a porta, porta errada ou porta TCP usada a partir do navegador |
ECONNREFUSED | Rede | Nada escutando naquele host e porta |
| Erro de certificado ou de handshake | TLS | CA não confiável, hostname divergente ou TLS contra uma porta sem criptografia |
| Conexão fechada logo após conectar | MQTT | CONNACK com código de erro — veja as tabelas abaixo |
| Conecta e depois cai repetidamente | Sessão | Client ID duplicado ou keep-alive mal configurado |
Timeouts de conexão
Um timeout significa que seus pacotes saíram e nada voltou. O broker nem teve a chance de dizer não. Verifique, nesta ordem:
- A porta corresponde ao transporte.
mqtt://na 1883,mqtts://na 8883,wss://na porta WebSocket do broker. Veja portas MQTT explicadas. - Sua rede permite a porta. Redes corporativas e de visitantes frequentemente bloqueiam 1883 e 8883. Tente
wss://na 443 para confirmar. - O broker está no ar. Sandboxes públicos reiniciam sem aviso; teste outro broker para descartar um problema na sua configuração.
- O firewall do seu servidor está aberto. Em brokers auto-hospedados, o security group da nuvem precisa permitir tráfego de entrada na porta do listener.
ECONNREFUSED
ECONNREFUSED é uma rejeição rápida e explícita do sistema operacional: o host é alcançável, mas nenhum processo está escutando naquela porta. Os motivos típicos são um broker que não está rodando, um listener vinculado apenas a 127.0.0.1, uma porta de container que nunca foi publicada (docker run -p 1883:1883) ou uma conexão a localhost de dentro de um container enquanto o broker está em outro.
Erros de TLS
- CA autoassinada ou desconhecida (
SELF_SIGNED_CERT_IN_CHAIN,UNABLE_TO_VERIFY_LEAF_SIGNATURE): forneça o arquivo da CA ao cliente. Não desative a verificação fora de testes locais. - Hostname divergente (
ERR_TLS_CERT_ALTNAME_INVALID): conecte usando o nome que consta no certificado, não um endereço IP ou alias interno. - Wrong version number / packet length too long: você está falando TLS com uma porta sem criptografia (por exemplo
mqtts://na 1883) ou MQTT puro com uma porta TLS. - Certificado expirado: confira as datas com
openssl s_client -connect host:8883 -servername host.
Erros exclusivos do navegador: mixed content e paths de WebSocket
Os navegadores só podem usar MQTT sobre WebSockets e acrescentam suas próprias regras. Uma página servida via HTTPS não pode abrir ws:// — o navegador bloqueia isso como mixed content antes de qualquer pacote ser enviado, e o erro só aparece no console do desenvolvedor. Use wss://. O outro erro frequente é a falta do path: muitos brokers esperam /mqtt e, sem ele, o upgrade para WebSocket falha com um 404. Saiba mais em MQTT sobre WebSockets.
Testar uma conexão wss que funciona
Códigos de retorno do CONNACK no MQTT 3.1.1
Se a rede e o transporte funcionam, o broker responde ao CONNECT com um CONNACK. No MQTT 3.1.1, ele traz um código de retorno de um byte:
| Código | Significado | O que corrigir |
|---|---|---|
0 | Conexão aceita | Nada — você está conectado |
1 | Versão de protocolo inaceitável | O broker não suporta a versão solicitada; alterne entre MQTT 3.1.1 e 5 |
2 | Identificador rejeitado | O client ID é inválido, longo demais, vazio quando não permitido ou bloqueado |
3 | Servidor indisponível | O broker está sobrecarregado ou em manutenção; tente de novo com backoff |
4 | Usuário ou senha incorretos | Verifique as credenciais e a codificação delas |
5 | Não autorizado | A conta não tem permissão para conectar; verifique as ACLs do broker ou a regra de client ID |
Reason codes comuns do MQTT 5
O MQTT 5 substitui os códigos de retorno por reason codes. Qualquer valor igual ou acima de 0x80 é uma falha. Os mesmos códigos podem aparecer no CONNACK ou em um DISCONNECT enviado pelo servidor:
| Código | Nome | Causa típica |
|---|---|---|
0x80 | Unspecified error | Falha genérica; verifique os logs do broker |
0x84 | Unsupported Protocol Version | O broker só fala 3.1.1 |
0x85 | Client Identifier not valid | Client ID malformado ou não permitido |
0x86 | Bad User Name or Password | Credenciais erradas |
0x87 | Not authorized | Usuário válido sem permissão para conectar |
0x88 | Server unavailable | O broker não pode aceitar conexões no momento |
0x8A | Banned | Cliente ou IP bloqueado pelo operador |
0x8C | Bad authentication method | Método de autenticação estendida não suportado |
0x95 | Packet too large | O CONNECT excede o tamanho máximo de pacote do broker |
0x97 | Quota exceeded | A conta atingiu um limite de conexões ou de uso |
0x9F | Connection rate exceeded | Reconexões rápidas demais; adicione backoff |
Outros dois aparecem em pacotes DISCONNECT durante uma sessão: 0x8D (Keep Alive timeout) e 0x8E (Session taken over). Ambos são tratados abaixo. Para uma visão geral do que mudou no MQTT 5, veja MQTT 5 vs 3.1.1.
Loops de disputa de client ID
A especificação exige que o broker desconecte um cliente existente quando chega uma nova conexão com o mesmo client ID. Se dois dispositivos — ou duas abas do navegador — compartilham um ID e ambos reconectam automaticamente, eles se revezam derrubando um ao outro a cada poucos segundos. No MQTT 5, o perdedor recebe um DISCONNECT com 0x8E; no 3.1.1, o socket simplesmente fecha.
Desconexões por keep-alive
O valor de keep-alive no CONNECT informa ao broker com que frequência esperar tráfego. Se ele não vê nada por uma vez e meia esse intervalo, fecha a conexão. Os clientes enviam PINGREQ quando estão ociosos, mas um event loop bloqueado, um dispositivo em modo de suspensão ou um idle timeout agressivo de NAT ou load balancer ainda podem derrubar a conexão. Mantenha o intervalo abaixo do idle timeout da sua rede (60 segundos é um padrão comum) e nunca bloqueie a thread que atende o cliente MQTT.
Registre o erro real
A maioria das bibliotecas esconde o motivo, a menos que você o escute. Com o 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
- Confirme o fluxo completo em um broker público que sabidamente funciona, usando o cliente online.
- Faça o esquema, a porta e o path corresponderem ao listener documentado do broker.
- Leia o código do CONNACK ou o reason code em vez de adivinhar.
- Use um client ID único e reconecte com backoff.
- Mantenha o keep-alive menor que qualquer idle timeout no caminho.
Para um procedimento de teste completo, siga como testar um broker MQTT.
Perguntas frequentes
O que significa “connection refused: not authorized” no MQTT?
O broker aceitou a conexão de rede, mas rejeitou o cliente. No MQTT 3.1.1 isso é o código de retorno 5 do CONNACK; no MQTT 5, o reason code 0x87. As credenciais podem até ser válidas, mas a conta não tem permissão para conectar ou para usar aquele client ID.
Por que meu cliente MQTT fica desconectando e reconectando?
A causa mais comum são dois clientes usando o mesmo client ID. O broker desconecta a sessão mais antiga sempre que uma nova se conecta com esse ID e, se os dois reconectam automaticamente, eles se derrubam um ao outro em loop. Dê a cada cliente um ID único.
Por que minha conexão MQTT dá timeout em vez de falhar?
Um timeout significa nenhuma resposta, o que normalmente aponta para um firewall descartando pacotes silenciosamente, um host errado ou o uso da porta TCP a partir do navegador. Uma recusa, por outro lado, significa que algo respondeu e disse não.