Le changement de callback API version de paho-mqtt 2.0 est de loin la raison la plus fréquente pour laquelle des scripts MQTT Python cessent soudainement de fonctionner après un pip install --upgrade. Ce guide explique précisément ce qui a changé dans paho-mqtt 2.0, vous donne la correction en une ligne pour l'erreur, puis détaille la migration de chaque callback vers les signatures VERSION2 recommandées. Vous terminerez avec un exemple complet et fonctionnel connecté à broker.connectmq.com que vous pourrez utiliser comme modèle.
Les erreurs que vous voyez probablement
Si vous arrivez ici depuis un moteur de recherche, vous avez très probablement l'une de ces erreurs :
ValueError: Unsupported callback API version: version 2.0 added a callback_api_version, see docs/migrations.rst for details
TypeError: Client.__init__() missing 1 required positional argument: 'callback_api_version'
DeprecationWarning: Callback API version 1 is deprecated, update to latest version
TypeError: on_connect() takes 4 positional arguments but 5 were given
Les deux premières viennent d'une création du client à la manière 1.x. La ValueError apparaît quand vous passez un client ID comme premier argument positionnel, comme dans mqtt.Client("my-device"), car cette position appartient désormais à la callback API version. La troisième est un avertissement indiquant que vous avez explicitement choisi l'ancienne API. La quatrième apparaît après le passage à VERSION2 si un callback a conservé l'ancien nombre de paramètres.
Ce qui a changé dans paho-mqtt 2.0
La version 2.0 du client Python Eclipse Paho, publiée début 2024, a été la première version majeure depuis des années. Son changement phare est que les callbacks sont désormais versionnés. En 1.x, les paramètres transmis aux callbacks différaient entre MQTT 3.1.1 et MQTT 5, et certains utilisaient de simples entiers tandis que d'autres utilisaient des objets reason code. La version 2 les unifie : chaque callback reçoit les mêmes paramètres quelle que soit la version du protocole, et les codes de résultat sont toujours des objets ReasonCode.
Pour éviter de casser silencieusement du code avec de nouvelles signatures, les mainteneurs vous obligent à choisir explicitement. Le premier argument de Client() est maintenant callback_api_version, avec deux valeurs possibles :
| Valeur | Comportement | Recommandée ? |
|---|---|---|
CallbackAPIVersion.VERSION1 |
Anciennes signatures de callbacks 1.x, émet un avertissement de dépréciation | Uniquement comme solution de transition temporaire |
CallbackAPIVersion.VERSION2 |
Nouvelles signatures unifiées, objets ReasonCode partout |
Oui, pour tout code nouveau ou migré |
Parmi les autres changements notables de la 2.0 figurent l'abandon de la prise en charge de Python 2.7 et des très anciennes versions de Python 3, ainsi que le renommage de la classe ReasonCodes en ReasonCode. La liste complète se trouve dans les notes de migration officielles de paho-mqtt.
Correction 1 : la solution de transition en une ligne (VERSION1)
Si la production est en panne et que vous devez la rétablir immédiatement, demandez à paho de conserver l'ancien comportement :
import paho.mqtt.client as mqtt
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, client_id="my-device")
Tous vos callbacks existants continuent de fonctionner sans modification. Vous verrez l'avertissement de dépréciation, qui vous rappelle que VERSION1 sera supprimée dans une future version majeure. Considérez cela comme un pansement, pas comme une correction.
Un autre pansement consiste à figer l'ancienne bibliothèque avec pip install "paho-mqtt<2.0". Cela fonctionne, mais bloque les correctifs de sécurité et de bugs, et entre souvent en conflit avec d'autres paquets qui exigent déjà la 2.x. Préférez le flag VERSION1 au gel de version, et planifiez la vraie migration.
Correction 2 : migrer vers la callback API version 2 de Paho MQTT 2.0
La vraie correction consiste à passer VERSION2 et à mettre à jour les signatures des callbacks. Voici tous les callbacks côte à côte :
| Callback | Signature 1.x / VERSION1 | Signature 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ée |
on_log |
(client, userdata, level, buf) |
inchangée |
Avec VERSION1 et MQTT 5, certains de ces callbacks avaient déjà des paramètres supplémentaires ; VERSION2 supprime cette différence, ce qui est tout l'objet du changement.
Liste de vérification de la migration
Parcourez votre code dans cet ordre :
- Remplacez chaque appel
mqtt.Client(...)parmqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=..., ...), en passant tous les autres arguments par mot-clé. - Ajoutez
propertiesàon_connectet renommezrcenreason_code. Remplacezrc == 0parnot reason_code.is_failure(la comparaison avec des entiers fonctionne toujours, mais l'attribut est plus lisible). - Remplacez
flags["session present"]parflags.session_present. - Donnez à
on_disconnectson nouveau paramètredisconnect_flagsainsi queproperties. - Dans
on_subscribe, remplacezgranted_qosparreason_code_listet vérifiez chaque entrée : les codes de succès ont des valeurs de 0 à 2 (la QoS accordée), les échecs ontis_failurepositionné. - Ajoutez
reason_codeetpropertiesàon_publishet àon_unsubscribe. - Recherchez
ReasonCodesdans le code et remplacez-le parReasonCode. - Exécutez vos tests en transformant les avertissements en erreurs (
python -W error::DeprecationWarning) pour prouver qu'il ne reste aucun client VERSION1.
Un exemple VERSION2 complet
Ce script utilise tous les callbacks courants avec les nouvelles signatures. Remplacez k7f3q2 par votre propre chaîne aléatoire pour ne pas partager vos topics avec d'autres utilisateurs du serveur MQTT public gratuit :
import time
import uuid
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
PREFIX = "connectmq/demo/k7f3q2"
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"Connect failed: {reason_code}")
return
print(f"Connected (session present: {flags.session_present})")
client.subscribe(f"{PREFIX}/#", qos=1)
def on_subscribe(client, userdata, mid, reason_code_list, properties):
for rc in reason_code_list:
print("Subscribe rejected:" if rc.is_failure else "Granted QoS:", rc)
def on_publish(client, userdata, mid, reason_code, properties):
print(f"Publish {mid} acknowledged: {reason_code}")
def on_message(client, userdata, msg):
print(f"{msg.topic}: {msg.payload.decode()}")
def on_disconnect(client, userdata, disconnect_flags, reason_code, properties):
print(f"Disconnected: {reason_code}")
client = mqtt.Client(
mqtt.CallbackAPIVersion.VERSION2,
client_id=f"migrate-{uuid.uuid4().hex[:8]}",
protocol=mqtt.MQTTv5,
)
client.on_connect = on_connect
client.on_subscribe = on_subscribe
client.on_publish = on_publish
client.on_message = on_message
client.on_disconnect = on_disconnect
client.connect(BROKER, 1883, keepalive=60)
client.loop_start()
time.sleep(1)
for i in range(3):
client.publish(f"{PREFIX}/test", f"hello {i}", qos=1)
time.sleep(1)
client.disconnect()
client.loop_stop()
Exécutez-le et vous devriez voir la connexion, la QoS accordée, trois accusés de publication et les trois messages renvoyés par le serveur public ConnectMQ. Passez protocol à mqtt.MQTTv311 et les mêmes callbacks fonctionnent toujours sans modification, ce qui est exactement la promesse de VERSION2. Si vous découvrez la bibliothèque elle-même, commencez plutôt par le tutoriel Paho MQTT Python.
Pièges après la migration
Bibliothèques tierces figées en 1.x. Certains paquets qui enveloppent paho ont déclaré paho-mqtt<2.0 dans leurs dépendances. Vérifiez avec pip show et pip check. Les versions récentes de la plupart des wrappers maintenus prennent en charge la 2.x ; mettez-les à jour, ou isolez les outils anciens dans leur propre environnement virtuel.
Arguments positionnels. Tout appel du type mqtt.Client("id", False, None, mqtt.MQTTv311) est désormais décalé d'une position. Convertissez-les tous en arguments nommés ; cela rendra les futures mises à niveau indolores.
Callbacks enregistrés avec des décorateurs. Le code qui utilise @client.connect_callback() et des décorateurs similaires doit lui aussi mettre à jour les signatures des fonctions décorées.
Exceptions avalées. Par défaut, paho journalise les exceptions levées dans les callbacks au lieu de faire planter votre programme. Une mauvaise signature peut donc ressembler à « il ne se passe rien ». Attachez un logger avec client.enable_logger() pendant la migration pour que le TypeError soit visible.
Logique de reconnexion. Si vous compariez des entiers rc dans on_disconnect pour décider de vous reconnecter, réécrivez cette logique autour de reason_code. Le guide de reconnexion paho-mqtt présente des patterns robustes, et le guide Python MQTT TLS couvre la connexion chiffrée à utiliser en production.
Faut-il envisager aiomqtt à la place ?
Si votre application repose sur asyncio, la migration des callbacks peut être le bon moment pour passer complètement à une API asynchrone. aiomqtt enveloppe paho et remplace les callbacks par async for message in client.messages. Notre tutoriel aiomqtt et asyncio montre le même flux publish/subscribe écrit de cette manière. Pour une comparaison plus large des clients Python et des autres langages, consultez la vue d'ensemble des bibliothèques clientes MQTT.
Testez votre code migré sur ConnectMQ
ConnectMQ exploite le serveur MQTT public gratuit broker.connectmq.com, idéal pour vérifier une migration mais pas pour des données de production, ainsi qu'un broker MQTT managé pour la production avec MQTT 3.1.1 et 5.0, TLS sur le port 8883 et des identifiants générés. Son portail inclut un explorateur de topics en direct pour confirmer que les clients migrés publient toujours ce que vous attendez. Quand vous êtes prêt à quitter le serveur public, faites tourner vos clients sur un broker MQTT cloud privé : inscrivez-vous à l'offre Serverless gratuite, sans carte bancaire.
Questions fréquentes
Qu'est-ce que callback_api_version dans paho-mqtt ?
C'est le premier argument de mqtt.Client() depuis paho-mqtt 2.0. Il sélectionne les signatures de callbacks utilisées par le client : VERSION1 pour l'ancien style 1.x ou VERSION2 pour le nouveau style unifié.
Faut-il utiliser VERSION1 ou VERSION2 ?
Utilisez VERSION2. VERSION1 n'existe que pour faire tourner l'ancien code et affiche un avertissement de dépréciation : elle finira donc par être supprimée.
Pourquoi est-ce que j'obtiens « Unsupported callback API version » en passant un client ID ?
Vous avez passé le client ID comme premier argument positionnel, désormais réservé à la callback API version. Passez d'abord mqtt.CallbackAPIVersion.VERSION2, puis l'ID sous la forme client_id=....
Faut-il modifier on_message lors de la migration ?
Non. on_message(client, userdata, message) conserve la même signature dans les deux versions. Les changements concernent les callbacks de connexion, de déconnexion, d'abonnement, de désabonnement et de publication.



