Solução de problemas

Erros de conexão MQTT: causas e soluções

Resolva erros de conexão MQTT rápido: timeouts, ECONNREFUSED, falhas de TLS, mixed content, códigos do CONNACK, reason codes do MQTT 5 e loops de client ID.

Atualizado · 8 min de leitura

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

SintomaCamadaCausa mais provável
Erro ENOTFOUND / getaddrinfoDNSErro de digitação no hostname, ou um esquema de URL incluído no campo de host
Fica travado e depois dá timeoutRedeFirewall descartando a porta, porta errada ou porta TCP usada a partir do navegador
ECONNREFUSEDRedeNada escutando naquele host e porta
Erro de certificado ou de handshakeTLSCA não confiável, hostname divergente ou TLS contra uma porta sem criptografia
Conexão fechada logo após conectarMQTTCONNACK com código de erro — veja as tabelas abaixo
Conecta e depois cai repetidamenteSessãoClient 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:

  1. A porta corresponde ao transporte. mqtt:// na 1883, mqtts:// na 8883, wss:// na porta WebSocket do broker. Veja portas MQTT explicadas.
  2. Sua rede permite a porta. Redes corporativas e de visitantes frequentemente bloqueiam 1883 e 8883. Tente wss:// na 443 para confirmar.
  3. O broker está no ar. Sandboxes públicos reiniciam sem aviso; teste outro broker para descartar um problema na sua configuração.
  4. 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ódigoSignificadoO que corrigir
0Conexão aceitaNada — você está conectado
1Versão de protocolo inaceitávelO broker não suporta a versão solicitada; alterne entre MQTT 3.1.1 e 5
2Identificador rejeitadoO client ID é inválido, longo demais, vazio quando não permitido ou bloqueado
3Servidor indisponívelO broker está sobrecarregado ou em manutenção; tente de novo com backoff
4Usuário ou senha incorretosVerifique as credenciais e a codificação delas
5Não autorizadoA 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ódigoNomeCausa típica
0x80Unspecified errorFalha genérica; verifique os logs do broker
0x84Unsupported Protocol VersionO broker só fala 3.1.1
0x85Client Identifier not validClient ID malformado ou não permitido
0x86Bad User Name or PasswordCredenciais erradas
0x87Not authorizedUsuário válido sem permissão para conectar
0x88Server unavailableO broker não pode aceitar conexões no momento
0x8ABannedCliente ou IP bloqueado pelo operador
0x8CBad authentication methodMétodo de autenticação estendida não suportado
0x95Packet too largeO CONNECT excede o tamanho máximo de pacote do broker
0x97Quota exceededA conta atingiu um limite de conexões ou de uso
0x9FConnection rate exceededReconexõ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:

debug.mjs
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

  1. Confirme o fluxo completo em um broker público que sabidamente funciona, usando o cliente online.
  2. Faça o esquema, a porta e o path corresponderem ao listener documentado do broker.
  3. Leia o código do CONNACK ou o reason code em vez de adivinhar.
  4. Use um client ID único e reconecte com backoff.
  5. 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.