Eine MQTT-Verbindung kann auf vier Ebenen scheitern: im Netzwerk (DNS und TCP), beim Transport (TLS oder WebSocket), beim MQTT-Handshake (CONNECT / CONNACK) und in der laufenden Session (Keep-Alive und Disconnects). Die richtige Ebene zu finden, ist schon die halbe Lösung. Dieser Guide geht jede davon durch – mit den genauen Fehlermeldungen und Reason Codes, die dir wahrscheinlich begegnen.
Schnelle Einordnung
| Symptom | Ebene | Wahrscheinlichste Ursache |
|---|---|---|
Fehler ENOTFOUND / getaddrinfo | DNS | Tippfehler im Hostnamen oder URL-Schema im Host-Feld angegeben |
| Hängt, dann Timeout | Netzwerk | Firewall verwirft den Port, falscher Port oder TCP-Port aus dem Browser verwendet |
ECONNREFUSED | Netzwerk | Auf diesem Host und Port lauscht nichts |
| Zertifikats- oder Handshake-Fehler | TLS | Nicht vertrauenswürdige CA, Hostname passt nicht oder TLS gegen einen unverschlüsselten Port |
| Verbindung direkt nach dem Connect geschlossen | MQTT | CONNACK mit Fehlercode – siehe Tabellen unten |
| Verbindet sich, bricht dann immer wieder ab | Session | Doppelte Client-ID oder falsch konfigurierter Keep-Alive |
Verbindungs-Timeouts
Ein Timeout bedeutet: Deine Pakete gingen raus, und nichts kam zurück. Der Broker hatte nie die Chance, Nein zu sagen. Prüfe der Reihe nach:
- Der Port passt zum Transport.
mqtt://auf 1883,mqtts://auf 8883,wss://auf dem WebSocket-Port des Brokers. Siehe MQTT-Ports erklärt. - Dein Netzwerk lässt den Port zu. Firmen- und Gastnetze blockieren oft 1883 und 8883. Teste
wss://auf 443, um das zu bestätigen. - Der Broker läuft. Öffentliche Sandboxes starten ohne Vorwarnung neu; teste einen anderen Broker, um dein eigenes Setup als Ursache auszuschließen.
- Deine Server-Firewall ist offen. Bei selbst gehosteten Brokern muss die Cloud-Security-Group eingehenden Traffic auf dem Listener-Port erlauben.
ECONNREFUSED
ECONNREFUSED ist eine schnelle, explizite Ablehnung durch das Betriebssystem: Der Host ist erreichbar, aber auf diesem Port lauscht kein Prozess. Typische Gründe sind ein Broker, der nicht läuft, ein Listener, der nur an 127.0.0.1 gebunden ist, ein Container-Port, der nie veröffentlicht wurde (docker run -p 1883:1883), oder ein Connect auf localhost aus einem Container heraus, während der Broker in einem anderen Container läuft.
TLS-Fehler
- Selbstsignierte oder unbekannte CA (
SELF_SIGNED_CERT_IN_CHAIN,UNABLE_TO_VERIFY_LEAF_SIGNATURE): Gib dem Client die CA-Datei. Deaktiviere die Prüfung nicht außerhalb lokaler Tests. - Hostname passt nicht (
ERR_TLS_CERT_ALTNAME_INVALID): Verbinde dich über den Namen im Zertifikat, nicht über eine IP-Adresse oder einen internen Alias. - Wrong version number / packet length too long: Du sprichst TLS mit einem unverschlüsselten Port (zum Beispiel
mqtts://auf 1883) oder unverschlüsseltes MQTT mit einem TLS-Port. - Abgelaufenes Zertifikat: Prüfe die Gültigkeitsdaten mit
openssl s_client -connect host:8883 -servername host.
Fehler nur im Browser: Mixed Content und WebSocket-Pfade
Browser können MQTT nur über WebSockets nutzen und bringen eigene Regeln mit. Eine über HTTPS ausgelieferte Seite kann kein ws:// öffnen – der Browser blockiert das als Mixed Content, bevor überhaupt ein Paket gesendet wird, und der Fehler taucht nur in der Entwicklerkonsole auf. Nutze wss://. Der andere häufige Fehler ist ein fehlender Pfad: Viele Broker erwarten /mqtt, und ohne ihn scheitert das WebSocket-Upgrade mit einem 404. Mehr dazu in MQTT über WebSockets.
Eine funktionierende wss-Verbindung testen
CONNACK-Return-Codes in MQTT 3.1.1
Funktionieren Netzwerk und Transport, antwortet der Broker auf CONNECT mit CONNACK. In MQTT 3.1.1 enthält es einen Return Code von einem Byte:
| Code | Bedeutung | Was zu tun ist |
|---|---|---|
0 | Verbindung akzeptiert (Connection accepted) | Nichts – du bist verbunden |
1 | Nicht akzeptierte Protokollversion (Unacceptable protocol version) | Der Broker unterstützt die angefragte Version nicht; wechsle zwischen MQTT 3.1.1 und 5 |
2 | Kennung abgelehnt (Identifier rejected) | Client-ID ist ungültig, zu lang, leer, obwohl das nicht erlaubt ist, oder gesperrt |
3 | Server nicht verfügbar (Server unavailable) | Broker ist überlastet oder in Wartung; mit Backoff erneut versuchen |
4 | Falscher Benutzername oder falsches Passwort (Bad user name or password) | Zugangsdaten und deren Kodierung prüfen |
5 | Nicht autorisiert (Not authorized) | Der Account darf sich nicht verbinden; Broker-ACLs oder die Client-ID-Regel prüfen |
Häufige MQTT-5-Reason-Codes
MQTT 5 ersetzt Return Codes durch Reason Codes. Alles ab 0x80 ist ein Fehler. Dieselben Codes können im CONNACK oder in einem vom Server gesendeten DISCONNECT auftauchen:
| Code | Name | Typische Ursache |
|---|---|---|
0x80 | Unspecified error | Allgemeiner Fehler; Broker-Logs prüfen |
0x84 | Unsupported Protocol Version | Broker spricht nur 3.1.1 |
0x85 | Client Identifier not valid | Fehlerhafte oder nicht erlaubte Client-ID |
0x86 | Bad User Name or Password | Falsche Zugangsdaten |
0x87 | Not authorized | Gültiger Benutzer ohne Berechtigung zum Verbinden |
0x88 | Server unavailable | Broker kann gerade keine Verbindungen annehmen |
0x8A | Banned | Client oder IP vom Betreiber gesperrt |
0x8C | Bad authentication method | Enhanced-Auth-Methode nicht unterstützt |
0x95 | Packet too large | CONNECT überschreitet die maximale Paketgröße des Brokers |
0x97 | Quota exceeded | Account hat ein Verbindungs- oder Nutzungslimit erreicht |
0x9F | Connection rate exceeded | Zu schnelle Reconnects; Backoff einbauen |
Zwei weitere tauchen während einer Session in DISCONNECT-Paketen auf: 0x8D (Keep Alive timeout) und 0x8E (Session taken over). Beide werden unten behandelt. Den Überblick darüber, was sich mit MQTT 5 geändert hat, findest du unter MQTT 5 vs. 3.1.1.
Übernahme-Schleifen durch Client-IDs
Die Spezifikation verlangt, dass ein Broker einen bestehenden Client trennt, sobald eine neue Verbindung mit derselben Client-ID eintrifft. Teilen sich zwei Geräte – oder zwei Browser-Tabs – eine ID und verbinden sich beide automatisch neu, werfen sie sich gegenseitig alle paar Sekunden hinaus. In MQTT 5 erhält der Verlierer ein DISCONNECT mit 0x8E; in 3.1.1 wird einfach der Socket geschlossen.
Disconnects durch Keep-Alive
Der Keep-Alive-Wert im CONNECT sagt dem Broker, wie oft er Traffic erwarten kann. Sieht er für das Anderthalbfache dieses Intervalls nichts, schließt er die Verbindung. Clients senden im Leerlauf ein PINGREQ, aber eine blockierte Event-Loop, ein schlafendes Gerät oder ein aggressives Idle-Timeout in NAT oder Load Balancer kann die Verbindung trotzdem kappen. Halte das Intervall unter dem Idle-Timeout deines Netzwerks (60 Sekunden sind ein üblicher Standard) und blockiere niemals den Thread, der den MQTT-Client bedient.
Den echten Fehler loggen
Die meisten Bibliotheken verschweigen den Grund, solange du nicht darauf hörst. Mit 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'));Checkliste
- Bestätige den Round Trip auf einem bekanntermaßen funktionierenden öffentlichen Broker im Online-Client.
- Gleiche Schema, Port und Pfad mit dem dokumentierten Listener des Brokers ab.
- Lies den CONNACK-Code oder Reason Code, statt zu raten.
- Nutze eine eindeutige Client-ID und verbinde dich mit Backoff neu.
- Halte den Keep-Alive kürzer als jedes Idle-Timeout auf der Strecke.
Ein vollständiges Testverfahren findest du unter MQTT-Broker testen.
Häufig gestellte Fragen
Was bedeutet bei MQTT „Connection refused: not authorized“?
Der Broker hat die Netzwerkverbindung angenommen, den Client aber abgelehnt. In MQTT 3.1.1 ist das CONNACK-Return-Code 5, in MQTT 5 Reason Code 0x87. Die Zugangsdaten können gültig sein, aber der Account darf sich nicht verbinden oder diese Client-ID nicht verwenden.
Warum trennt sich mein MQTT-Client ständig und verbindet sich neu?
Die häufigste Ursache sind zwei Clients mit derselben Client-ID. Der Broker trennt die ältere Session, sobald sich eine neue mit dieser ID verbindet, und wenn sich beide automatisch neu verbinden, werfen sie sich in einer Schleife gegenseitig hinaus. Gib jedem Client eine eindeutige ID.
Warum läuft meine MQTT-Verbindung in einen Timeout, statt abgelehnt zu werden?
Ein Timeout bedeutet, dass überhaupt keine Antwort kam – das deutet meist auf eine Firewall hin, die Pakete stillschweigend verwirft, auf einen falschen Host oder darauf, dass der TCP-Port aus einem Browser genutzt wird. Eine Ablehnung heißt dagegen, dass etwas geantwortet und Nein gesagt hat.