@@ -140,6 +140,7 @@ def _create_mqtt_client(self):
140140 if self ._websockets :
141141 logger .info ("Creating client for connecting using MQTT over websockets" )
142142 mqtt_client = mqtt .Client (
143+ callback_api_version = mqtt .CallbackAPIVersion .VERSION1 ,
143144 client_id = self ._client_id ,
144145 clean_session = False ,
145146 protocol = mqtt .MQTTv311 ,
@@ -149,7 +150,10 @@ def _create_mqtt_client(self):
149150 else :
150151 logger .info ("Creating client for connecting using MQTT over TCP" )
151152 mqtt_client = mqtt .Client (
152- client_id = self ._client_id , clean_session = False , protocol = mqtt .MQTTv311
153+ callback_api_version = mqtt .CallbackAPIVersion .VERSION1 ,
154+ client_id = self ._client_id ,
155+ clean_session = False ,
156+ protocol = mqtt .MQTTv311 ,
153157 )
154158
155159 if self ._proxy_options :
@@ -436,7 +440,6 @@ def disconnect(self, clear_inflight=False):
436440 :raises: ConnectionDroppedError in unexpected cases.
437441 :raises: UnauthorizedError in unexpected cases.
438442 :raises: ConnectionFailedError in unexpected cases.
439- :raises: NoConnectionError if the client isn't actually connected.
440443 """
441444 logger .info ("disconnecting MQTT client" )
442445 try :
@@ -452,11 +455,22 @@ def disconnect(self, clear_inflight=False):
452455
453456 logger .debug ("_mqtt_client.disconnect returned rc={}" .format (rc ))
454457 if rc :
455- # This could result in ConnectionDroppedError or ProtocolClientError
456- # No matter what, we always raise here to give upper layers a chance to respond
457- # to this error.
458- err = _create_error_from_rc_code (rc )
459- raise err
458+ # Special case: MQTT_ERR_NO_CONN (rc=4) during disconnect means the socket
459+ # is already closed. In Paho 2.x, this can happen even after a successful
460+ # disconnect because the on_disconnect callback fires (with rc=0) before
461+ # disconnect() returns, and Paho's internal cleanup closes the socket.
462+ # Since we wanted to disconnect and we're disconnected, treat this as success.
463+ if rc == mqtt .MQTT_ERR_NO_CONN :
464+ logger .debug (
465+ "disconnect returned MQTT_ERR_NO_CONN - socket already closed, treating as success"
466+ )
467+ # Still clear inflight operations since we're effectively disconnected
468+ if clear_inflight :
469+ self ._op_manager .cancel_all_operations ()
470+ else :
471+ # This could result in ConnectionDroppedError or ProtocolClientError
472+ err = _create_error_from_rc_code (rc )
473+ raise err
460474 else :
461475 # Clear pending ops if instructed, but only if the disconnect was successful.
462476 # Technically the disconnect could still fail upon response, however that would then
0 commit comments