Azure IoT Edge Update Twin Errors

Reading time ~3 minutes

Want to learn more about Azure? Sign up for my mailing list!

I’ve been working more with Azure IoT Edge Devices recently and with versions of the IoT Hub EdgeAgent and the Python SDKs I noticed a few very non-descriptive errors when trying to patch device properties with the patch_twin_reported_properties() method.

Here’s a quick look at them in case anyone else notices these later on.

I tried to catch and log a bit more about them here:

Exception when patching twin reported properties: ClientError('Unexpected failure') caused by ServiceError('twin operation returned status 400')
Error 400 received from twin operation
response body: b''
PatchTwinReportedPropertiesOperation: completing with error twin operation returned status 400 caused by None
Callback completed with error twin operation returned status 400 caused by None
azure.iot.device.exceptions.ServiceError: twin operation returned status 400 caused by None
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/azure/iot/device/iothub/sync_clients.py", line 32, in handle_result
    return callback.wait_for_completion()
  File "/usr/local/lib/python3.7/site-packages/azure/iot/device/common/evented_callback.py", line 70, in wait_for_completion
    raise self.exception
azure.iot.device.exceptions.ServiceError: twin operation returned status 400 caused by None

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "./whereicalledit.py", line 90, in <module>
    main()
  File "./whereicalledit.py", line 70, in main
    update_device_properties(module_client)
  File "./whereicalledit.py", line 57, in update_device_properties
    client.patch_twin_reported_properties(patch_payload)
  File "<string>", line 1, in patch_twin_reported_properties
  File "/usr/local/lib/python3.7/site-packages/azure/iot/device/iothub/sync_clients.py", line 295, in patch_twin_reported_properties
    handle_result(callback)
  File "/usr/local/lib/python3.7/site-packages/azure/iot/device/iothub/sync_clients.py", line 42, in handle_result
    raise exceptions.ClientError(message="Unexpected failure", cause=e)
azure.iot.device.exceptions.ClientError: Unexpected failure caused by twin operation returned status 400 caused by None

After extensive searching I couldn’t find many related explanations for:

Unexpected failure caused by twin operation returned status 400 caused by None

After more debugging I determined there were a few possible causes for these errors:

Invalid data types

The Azure IoT Hub Device and Module Twins are finnicky beasts. With strangely limited ability to store some data types depending on the version of the IoT Edge version and Edge Agent version you are using.

Specifically, if you’re using Python lists/arrays/tuples or data that contains nested list/arrays/tuples this might be what’s causing the issue.

You might be able to fix this by upgrading to a more recent version of the Azure IoT Edge Agent or the SDKs but this is only a possibility.

One way to easily coerce your data to be a little nicer (removing tuples at least) is to:

import json

from azure.iot.hub import IoTHubRegistryManager

module_client = IoTHubRegistryManager("CONNECTION_STRING")

payload = {
    "my_key": {
      "my_bad_tuple": ("thing1", "thing2")
    }
}

payload = json.loads(json.dumps(payload))

# print(payload)
# {
#     "my_key": {
#         "my_bad_tuple": ["thing1", "thing2"]
#     }
# }
# The tuple is now a list

module_client.patch_twin_reported_properties(payload)

Doing a conversion like this might help you coerce a bunch of bad types to what you need to avoid this issue.

But, you might also have nested arrays/lists that IoT Hub still doesn’t like. In that case. Your best bet might just be to leave them as a JSON string and load them back out when required:

import json

from azure.iot.hub import IoTHubRegistryManager

module_client = IoTHubRegistryManager("CONNECTION_STRING")

payload = {
    "my_key": {
      "my_bad_tuple": ("thing1", "thing2")
    }
}

payload["my_key"] = json.dumps(payload["my_key"])
module_client.patch_twin_reported_properties(payload)

I hope this helps you if you’re stuck in an Azure IoT Hub debugging black hole with Azure IoT Edge!

Have you seen similar issues? What helped solve them for you?

Have questions or comments or just want to learn more? Join me (Fernando) in the Moonrise Labs Discord!