paho-mqtt 2.0 a changé la façon de créer un client et ce que reçoivent vos callbacks. Si votre script ne fonctionne plus après un pip install --upgrade, la correction est presque toujours la même : passez mqtt.CallbackAPIVersion.VERSION2 en premier argument de mqtt.Client() et mettez à jour les signatures des callbacks. Ce guide présente les erreurs exactes, ce qui a changé et un exemple complet avant/après.
Les erreurs que vous voyez probablement
| Erreur | Cause | Correction |
|---|---|---|
ValueError: Unsupported callback API version: version 2.0 added a callback_api_version, see docs/migrations.rst for details | Le client ID a été passé en premier argument positionnel : mqtt.Client("my-id") | mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="my-id") |
DeprecationWarning: Callback API version 1 is deprecated, update to latest version | mqtt.Client() sans version, ou avec VERSION1 | Passez VERSION2 et mettez à jour les callbacks |
TypeError: on_connect() takes 4 positional arguments but 5 were given | VERSION2 avec un on_connect(client, userdata, flags, rc) façon 1.x | Ajoutez le paramètre properties (voir plus bas) |
TypeError: on_disconnect() takes 3 positional arguments but 5 were given | Idem pour on_disconnect(client, userdata, rc) | Nouvelle signature avec disconnect_flags, reason_code, properties |
AttributeError: module 'paho.mqtt.client' has no attribute 'CallbackAPIVersion' | Votre code vise la 2.x mais l'environnement a encore la 1.x | pip install "paho-mqtt>=2.0" et vérifiez quel interpréteur exécute le script |
Vérifiez d'abord la version installée — environnements virtuels et Python système divergent souvent :
python -c "import paho.mqtt; print(paho.mqtt.__version__)"
pip show paho-mqttCe qui a changé dans la 2.0
La version 1.x avait un seul style de callbacks dont les arguments dépendaient de la version du protocole : les callbacks MQTT 3.1.1 recevaient un entier rc, ceux de MQTT 5 recevaient reason_code et properties. Il était donc facile d'écrire du code qui marchait avec un protocole et plantait avec l'autre.
La 2.0 introduit les versions de l'API de callbacks. Avec CallbackAPIVersion.VERSION2, chaque callback a la même signature en MQTT 3.1.1 et MQTT 5, les reason codes sont toujours des objets ReasonCode et les properties sont toujours passées (None quand le protocole n'en a pas). Le constructeur ressemble désormais à ceci :
mqtt.Client(
callback_api_version, # nouveau, premier argument positionnel
client_id="",
clean_session=None,
userdata=None,
protocol=mqtt.MQTTv311,
transport="tcp",
reconnect_on_failure=True,
manual_ack=False,
)À savoir également : la 2.x exige Python 3.7 ou plus récent, et les helpers publish.single() / subscribe.simple() gèrent la version pour vous ; le code qui n'utilise qu'eux continue donc généralement de fonctionner.
Signatures des callbacks : 1.x vs VERSION2
| Callback | 1.x (MQTT 3.1.1) | 2.x avec VERSION2 |
|---|---|---|
on_connect | (client, userdata, flags, rc) | (client, userdata, flags, reason_code, properties) |
on_disconnect | (client, userdata, rc) | (client, userdata, disconnect_flags, reason_code, properties) |
on_subscribe | (client, userdata, mid, granted_qos) | (client, userdata, mid, reason_code_list, properties) |
on_unsubscribe | (client, userdata, mid) | (client, userdata, mid, reason_code_list, properties) |
on_publish | (client, userdata, mid) | (client, userdata, mid, reason_code, properties) |
on_message | (client, userdata, message) | inchangé |
on_log | (client, userdata, level, buf) | inchangé |
En pratique, trois détails comptent :
flagsn'est plus un dict. Danson_connect, c'est un objetConnectFlags: utilisezflags.session_presentau lieu deflags["session present"].disconnect_flags.is_disconnect_packet_from_serverindique si le broker a envoyé un DISCONNECT (MQTT 5) ou si la bibliothèque a détecté elle-même la perte de connexion.on_subscribereçoit des reason codes, pas des entiers de QoS. Pour un abonnement accordé en QoS 1, lavaluedu reason code vaut1; un abonnement refusé a une valeur de0x80ou plus, doncrc.is_failureest vrai.
Les reason codes sont désormais des objets
Avec VERSION2, reason_code est un ReasonCode. Il est égal à un entier comme à son nom, s'affiche sous forme de nom et possède la propriété is_failure :
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"échec de connexion : {reason_code} ({reason_code.value})")
return
print("connecté, session présente :", flags.session_present)reason_code == 0 et reason_code == "Success" fonctionnent toujours pour le cas de succès : le test le plus courant survit donc à la mise à jour sans modification.
Un exemple complet avant/après
Voici du code 1.x typique — il échoue en 2.x avec le ValueError ci-dessus :
import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, rc):
if rc == 0:
client.subscribe("testmqtt/paho2/#", qos=1)
else:
print("connect failed, rc =", rc)
def on_disconnect(client, userdata, rc):
print("disconnected, rc =", rc)
def on_message(client, userdata, msg):
print(msg.topic, msg.payload.decode())
client = mqtt.Client("testmqtt-paho2-demo")
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.on_message = on_message
client.connect("broker.connectmq.com", 1883, 60)
client.loop_forever()Le même client migré vers VERSION2 :
import os
import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"échec de connexion : {reason_code}")
return
print("connecté, session présente :", flags.session_present)
client.subscribe("testmqtt/paho2/#", qos=1)
def on_subscribe(client, userdata, mid, reason_code_list, properties):
for rc in reason_code_list:
print("abonnement :", "refusé" if rc.is_failure else f"QoS {rc.value} accordé")
def on_disconnect(client, userdata, disconnect_flags, reason_code, properties):
print(f"déconnecté : {reason_code} (par le serveur : {disconnect_flags.is_disconnect_packet_from_server})")
def on_message(client, userdata, msg):
print(msg.topic, msg.payload.decode())
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="testmqtt-paho2-demo")
if os.environ.get("MQTT_USER"):
client.username_pw_set(os.environ["MQTT_USER"], os.environ.get("MQTT_PASS"))
client.on_connect = on_connect
client.on_subscribe = on_subscribe
client.on_disconnect = on_disconnect
client.on_message = on_message
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_forever()Les mêmes callbacks fonctionnent sans changement avec protocol=mqtt.MQTTv5 — c'est le principal avantage de VERSION2. Seuls les arguments du constructeur diffèrent : MQTT 5 n'accepte pas clean_session (il lève ValueError: Clean session is not used for MQTT 5.0) ; utilisez plutôt clean_start et l'expiration de session dans connect(). Le guide de migration de MQTT 3.1.1 vers MQTT 5 détaille ces options.
Checklist de migration
- Épinglez la version cible, par exemple
paho-mqtt>=2.1,<3dansrequirements.txt, pour que la prochaine version majeure ne vous prenne pas par surprise. - Cherchez
mqtt.Client(dans le code et passez d'abordmqtt.CallbackAPIVersion.VERSION2, avecclient_id=en argument nommé. - Mettez à jour chaque callback vers la nouvelle signature — y compris ceux assignés dans d'autres modules ou bibliothèques.
- Remplacez les comparaisons d'entiers sur
rcparreason_code.is_failureou des comparaisons de noms. - Remplacez
flags["session present"]parflags.session_present. - Exécutez avec les warnings visibles (
python -W error::DeprecationWarning app.py) pour repérer tout client resté en VERSION1.
Si vous réécrivez beaucoup de code de toute façon, demandez-vous si un client asynchrone ne serait pas plus adapté — aiomqtt pour asyncio repose sur paho-mqtt 2.x. Pour les autres exceptions de paho, consultez les erreurs paho-mqtt en Python.
Tester le client migré
Lancez client_v2.py, ouvrez le client MQTT en ligne dans votre navigateur, connectez-vous à broker.connectmq.com et publiez sur testmqtt/paho2/hello. Votre script doit afficher le topic et le payload. Pour tester les cas d'erreur, connectez-vous à votre propre broker avec un mauvais mot de passe et vérifiez que on_connect affiche un motif lisible comme Bad user name or password plutôt qu'un simple nombre.
Publier un message de test vers le client migré
La migration vers paho-mqtt 2.0 est surtout mécanique : un nouvel argument de constructeur, cinq signatures de callbacks et quelques tests d'entiers qui deviennent des tests de reason codes. Faites-la une fois avec VERSION2, épinglez la version majeure, et vos callbacks se comporteront de la même façon en MQTT 3.1.1 et en MQTT 5. Pour un tour complet de la bibliothèque, consultez le guide MQTT avec Python.
Questions fréquentes
Que signifie 'Unsupported callback API version' dans paho-mqtt ?
Depuis paho-mqtt 2.0, le premier argument de mqtt.Client() est la version de l'API de callbacks. Le code écrit pour la 1.x qui passe le client ID en premier argument positionnel lève désormais ce ValueError. Passez d'abord mqtt.CallbackAPIVersion.VERSION2, puis le client ID via client_id=.
Puis-je garder mes anciens callbacks avec paho-mqtt 2.x ?
Oui, temporairement. Avec mqtt.CallbackAPIVersion.VERSION1, les signatures de callbacks de la 1.x continuent de fonctionner, mais la bibliothèque émet un DeprecationWarning et la version 1 doit être supprimée dans une future version majeure. Migrez vers VERSION2 dès que possible.
Pourquoi mon test 'if rc == 5' ne fonctionne-t-il plus après la mise à jour ?
Avec VERSION2, on_connect reçoit un objet ReasonCode même en MQTT 3.1.1, et les codes de retour 3.1.1 sont convertis en reason codes MQTT 5. 'Not authorized' vaut 135 (0x87), pas 5. Utilisez reason_code.is_failure ou comparez avec le nom, par exemple reason_code == 'Not authorized'.

