Context
- OS and version used: Alpine Linux Container on a Linux distribution running Azure IoT Edge.
- Node.js version: 24
- npm version: 11.16.0
- list of installed packages:
+-- azure-iot-device-mqtt@1.16.4
+-- azure-iot-device@1.18.4
Description of the issue
The module twin functionality of the Azure IoT SDK stops working completely when edgeHub is stopped and removed on the background. This only seems to occur with the MQTT transport. We have tested this on AMQP as well and it does not occur there.
We have adjusted the sample code to reproduce the issue.
The sample code updates the twin whenever a direct method is received. A counter is incremented each time the twin gets updated. This works fine on an initial start. When invoking the direct method a couple of times the logs state:
09:17:24 test TestModule: test called
09:17:24 test TestModule: Successfully sent method response
09:17:24 test TestModule: Updating reported properties with counter: 0
09:17:24 test TestModule: Reported properties successfully updated with counter: 0
09:17:26 test TestModule: test called
09:17:26 test TestModule: Successfully sent method response
09:17:26 test TestModule: Updating reported properties with counter: 1
09:17:26 test TestModule: Reported properties successfully updated with counter: 1
Then on the background we stop and remove edgeHub (for example docker stop edgeHub && docker rm edgeHub.
After trying to invoke the direct methods again:
09:18:09 test TestModule: Connected
09:18:25 test TestModule: test called
09:18:25 test TestModule: Successfully sent method response
09:18:29 test TestModule: test called
09:18:29 test TestModule: Successfully sent method response
Notice that there are no logs regarding anything twin related anymore.
Code sample exhibiting the issue
Sample code
'use strict';
const Protocol = require('azure-iot-device-mqtt').Mqtt;
const ModuleClient = require('azure-iot-device').ModuleClient;
let counter = 0;
ModuleClient.fromEnvironment(Protocol, function (err, client) {
if (err) {
console.error("Could not create client: " + err.toString());
process.exit(-1);
} else {
console.log('got client');
client.on('error', function (err) {
console.error(err.message);
});
client.on('connect', function () {
console.log("Connected");
})
client.on('disconnect', function () {
console.log("Disconnected");
})
// connect to the edge instance
client.open(function (err) {
if (err) {
console.error('could not open IotHub client');
} else {
console.log('client opened');
client.onMethod('test', function(request, response) {
console.log('test called')
// Create device Twin
client.getTwin(function (err, twin) {
if (err) {
console.error('could not get twin');
} else {
console.log(`Updating reported properties with counter: ${counter}`)
twin.properties.reported.update({counter: counter}, function (err) {
if (err) throw err;
console.log(`Reported properties successfully updated with counter: ${counter}`);
counter+=1
});
}
});
response.send(204, {}, function(err) {
if (err) {
console.error(err)
} else {
console.log("Successfully sent method response")
}
})
});
}
});
}
});
Console log of the issue
Spend a considerable amount of time going through the logs and noticed that we never seem to get a response to a call for getting the twin. The debug logs state:
09:35:20 test TestModule[773]: 2026-06-29T09:35:20.091Z mqttjs:client publish :: message ` ` to topic `$iothub/twin/GET/?$rid=0dea13f2-8648-41d5-bd49-4171c33434c6`
09:35:20 test TestModule[773]: 2026-06-29T09:35:20.091Z mqttjs:client publish :: qos 0
09:35:20 test TestModule[773]: 2026-06-29T09:35:20.091Z mqttjs:client MqttClient:publish: packet cmd: publish
09:35:20 test TestModule[773]: 2026-06-29T09:35:20.091Z mqttjs:client _sendPacket :: (test/TestModule) :: start
09:35:20 test TestModule[773]: 2026-06-29T09:35:20.091Z mqttjs:client sendPacket :: packet: {
09:35:20 test TestModule[773]: cmd: 'publish',
09:35:20 test TestModule[773]: topic: '$iothub/twin/GET/?$rid=0dea13f2-8648-41d5-bd49-4171c33434c6',
09:35:20 test TestModule[773]: payload: ' ',
09:35:20 test TestModule[773]: qos: 0,
09:35:20 test TestModule[773]: retain: false,
09:35:20 test TestModule[773]: messageId: 0,
09:35:20 test TestModule[773]: dup: false
09:35:20 test TestModule[773]: }
We would expect the following to be logged but it never comes.
mqtt-packet:parser _parseString: result: $iothub/twin/res/200/?$rid=0dea13f2-8648-41d5-bd49-4171c33434c6
When looking at edgeHub debug logs the response is actually written so the SDK does not receive it somehow.
It seems that the internal mqtt twin client (mqtt_twin_client.js) does not resubscribe on the response topic of the twin upon a complete disconnect. The internal mqtt twin client seems to think it is still subscribed to the response topic even though it is not. We verified this by manually adding the following to the moduleClient.on('connect').
client.on('connect', function () {
console.log("Connected");
client._transport._twinClient._topicFsm.unsubscribe(moduleClient._transport._twinClient._responseTopic, () => {
console.log("unsubscribed on response topic");
})
})
This forces the internal state machine of the twin client to unsubscribe on the topic so when a subsequent call comes in for getTwin or anything twin related, it will actually subscribe to the topic again.
Workaround
Force the internal state machine of the mqtt twin client to unsubscribe on a connect. Any subsequent operation on the twin afterwards should subscribe to it again, solving the issue. This is very far from ideal, but it does seem to work and solve the issue.
client.on('connect', function () {
client._transport._twinClient._topicFsm.unsubscribe(moduleClient._transport._twinClient._responseTopic, () => {
console.log("unsubscribed on response topic");
})
})
Context
+-- azure-iot-device-mqtt@1.16.4
+-- azure-iot-device@1.18.4
Description of the issue
The module twin functionality of the Azure IoT SDK stops working completely when edgeHub is stopped and removed on the background. This only seems to occur with the MQTT transport. We have tested this on AMQP as well and it does not occur there.
We have adjusted the sample code to reproduce the issue.
The sample code updates the twin whenever a direct method is received. A counter is incremented each time the twin gets updated. This works fine on an initial start. When invoking the direct method a couple of times the logs state:
Then on the background we stop and remove edgeHub (for example
docker stop edgeHub && docker rm edgeHub.After trying to invoke the direct methods again:
Notice that there are no logs regarding anything twin related anymore.
Code sample exhibiting the issue
Sample code
Console log of the issue
Spend a considerable amount of time going through the logs and noticed that we never seem to get a response to a call for getting the twin. The debug logs state:
We would expect the following to be logged but it never comes.
When looking at edgeHub debug logs the response is actually written so the SDK does not receive it somehow.
It seems that the internal mqtt twin client (
mqtt_twin_client.js) does not resubscribe on the response topic of the twin upon a complete disconnect. The internal mqtt twin client seems to think it is still subscribed to the response topic even though it is not. We verified this by manually adding the following to themoduleClient.on('connect').This forces the internal state machine of the twin client to unsubscribe on the topic so when a subsequent call comes in for
getTwinor anything twin related, it will actually subscribe to the topic again.Workaround
Force the internal state machine of the mqtt twin client to unsubscribe on a connect. Any subsequent operation on the twin afterwards should subscribe to it again, solving the issue. This is very far from ideal, but it does seem to work and solve the issue.