Skip to content

Matter Switch: Descriptor.PartsList handler crashes during Matter camera onboarding when ELECTRICAL_SENSOR_EPS is nil #2880

@ldeora

Description

@ldeora

Summary

When onboarding an Aqara Camera Hub G350 through the Matter Switch Edge Driver, the camera is recognized correctly and the camera sub-driver loads, but the driver throws repeated runtime errors while handling Descriptor.PartsList.

The crash appears to come from the generic switch_handlers/attribute_handlers.lua parts_list_handler, which assumes device:get_field(fields.ELECTRICAL_SENSOR_EPS) is a table and later evaluates #tree_topology_eps even when that field is nil.

This looks like a driver-side nil-guard bug in the generic Matter Switch Descriptor.PartsList path, not a malformed response from the camera.

Environment

  • Driver: matter-switch
  • Protocol: Matter
  • Device: Aqara Camera Hub G350
  • User-visible name in logs: Aqara Camera Hub G350
  • BasicInformation.SoftwareVersion: 4005020
  • Observed endpoint topology from logs:
    • EP0: DeviceType 0x0016
    • EP1: DeviceType 0x000E
    • EP2: DeviceType 0x0142 (Camera)

Expected behavior

Descriptor.PartsList reports for devices that do not participate in electrical sensor / power topology profiling should be ignored safely, or at minimum should not crash if fields.ELECTRICAL_SENSOR_EPS was never initialized.

The device should complete onboarding without repeated thread exceptions.

Actual behavior

The device is added, sub_drivers.camera is lazy-loaded, the profile is updated to camera, and camera capabilities are enabled successfully. However, repeated thread exceptions are thrown while processing Descriptor.PartsList.

Representative runtime error:

[string "switch_handlers/attribute_handlers.lua"]:359: attempt to get length of a nil value (local 'tree_topology_eps')

Reproduction steps

  1. Pair/onboard an Aqara Camera Hub G350 to SmartThings over Matter.

  2. Watch hub logs during onboarding.

  3. Observe:

    • the camera sub-driver is loaded,
    • the profile is updated to camera,
    • camera attributes are read successfully,
    • then repeated exceptions occur in the generic Descriptor.PartsList handler.

Relevant log excerpts

INFO Matter Switch  <MatterDevice ... (Aqara Camera Hub G350)> Lazy loaded driver into dispatcher: sub_drivers.camera
INFO Matter Switch  <MatterDevice ... (Aqara Camera Hub G350)> Updating device profile to camera. Enabling the optional capabilities [webrtc, cameraViewportSettings, videoStreamSettings, imageCapture, nightVision] on component 'main', [audioMute, audioVolume] on component 'speaker', and [audioMute, audioVolume] on component 'microphone'

ERROR Matter Switch  Aqara Camera Hub G350 thread encountered error:
[string "st/dispatcher.lua"]:270: Error encountered while processing event for <MatterDevice ... (Aqara Camera Hub G350)>:
...
"[string "switch_handlers/attribute_handlers.lua"]:359: attempt to get length of a nil value (local 'tree_topology_eps')"

The same onboarding session also shows successful camera-specific responses, for example:

TwoWayTalkSupport = FULL_DUPLEX
NightVision = AUTO
SpeakerMuted = false
SpeakerVolumeLevel = 80
MicrophoneMuted = false
MicrophoneVolumeLevel = 100

And SmartThings emits the corresponding camera capability events:

webrtc.talkback = true
webrtc.talkbackDuplex = fullDuplex
nightVision = auto
audioMute/audioVolume for speaker and microphone

Analysis

1) The generic main driver is handling Descriptor.PartsList

The main matter-switch driver registers the generic Descriptor.PartsList handler here:

  • drivers/SmartThings/matter-switch/src/init.lua
  • clusters.Descriptor.attributes.PartsList.ID -> attribute_handlers.parts_list_handler

2) parts_list_handler is not nil-safe

In drivers/SmartThings/matter-switch/src/switch_handlers/attribute_handlers.lua, the handler does this:

local tree_topology_eps = device:get_field(fields.ELECTRICAL_SENSOR_EPS)

for i, tree_ep_info in pairs(tree_topology_eps or {}) do
  ...
end

if #tree_topology_eps == 0 then
  ...
end

If fields.ELECTRICAL_SENSOR_EPS was never initialized, this becomes:

  • iteration over pairs(tree_topology_eps or {}) -> safe
  • #tree_topology_eps -> crashes on nil

That exactly matches the observed runtime error.

function AttributeHandlers.parts_list_handler(driver, device, ib, response)
if device:get_field(fields.profiling_data.POWER_TOPOLOGY) ~= nil then
device.log.warn("Received a PartsList response after power topology has already been determined. Ignoring this response.")
return
end
local tree_topology_eps = device:get_field(fields.ELECTRICAL_SENSOR_EPS)
for i, tree_ep_info in pairs(tree_topology_eps or {}) do
if ib.endpoint_id == tree_ep_info.endpoint_id then
-- since EP response is being handled here, remove it from the ELECTRICAL_SENSOR_EPS table
switch_utils.remove_field_index(device, fields.ELECTRICAL_SENSOR_EPS, i)
local associated_endpoints_ids = {}
for _, element in pairs(ib.data.elements or {}) do
table.insert(associated_endpoints_ids, element.value)
end
-- set the required profile elements ("-power", etc.) to one of these EP IDs for later profiling.
-- set an assigned child key in the case this will emit events on an EDGE_CHILD device
switch_utils.set_fields_for_electrical_sensor_endpoint(device, tree_ep_info, associated_endpoints_ids)
break
end
end
if #tree_topology_eps == 0 then -- in other words, all PartsList attribute responses for TREE Electrical Sensor EPs have been handled
device:set_field(fields.profiling_data.POWER_TOPOLOGY, clusters.PowerTopology.types.Feature.TREE_TOPOLOGY, {persist=true})
device_cfg.match_profile(driver, device)
end
end

3) There appears to be a second similar nil hazard

available_endpoints_handler appears to have the same pattern:

local set_topology_eps = device:get_field(fields.ELECTRICAL_SENSOR_EPS)

for i, set_ep_info in pairs(set_topology_eps or {}) do
  ...
end

if #set_topology_eps == 0 then
  ...
end

So the same nil-guard fix may be needed there as well.

4) This does not look like an Aqara camera payload problem

The device successfully returns:

  • Descriptor.DeviceTypeList
  • Descriptor.PartsList
  • CameraAvStreamManagement.AttributeList
  • TwoWayTalkSupport
  • NightVision
  • Speaker* / Microphone* attributes

The camera sub-driver also updates the profile and emits expected camera capability events.

So the issue appears to be:

  • normal Matter responses from the device
  • generic main-driver descriptor handling crashes on missing electrical topology state

5) Why this may affect more than this one device

This may affect any Matter device that:

  • triggers the generic Descriptor.PartsList path in the main driver,
  • but never initializes fields.ELECTRICAL_SENSOR_EPS.

A Matter camera is one clear example because it loads the camera sub-driver but still seems to receive the generic main-driver Descriptor.PartsList handling.

Suggested fix

At minimum, both handlers should early-return unless fields.ELECTRICAL_SENSOR_EPS is a table.

For example:

local tree_topology_eps = device:get_field(fields.ELECTRICAL_SENSOR_EPS)
if type(tree_topology_eps) ~= "table" then
  return
end

and similarly:

local set_topology_eps = device:get_field(fields.ELECTRICAL_SENSOR_EPS)
if type(set_topology_eps) ~= "table" then
  return
end

Alternatively:

@@ -316,7 +316,7 @@
 --- In the case there are multiple endpoints supporting the PowerTopology cluster with
 --- SET feature, all AvailableEndpoints responses must be handled before profiling.
 function AttributeHandlers.available_endpoints_handler(driver, device, ib, response)
-  local set_topology_eps = device:get_field(fields.ELECTRICAL_SENSOR_EPS)
+  local set_topology_eps = device:get_field(fields.ELECTRICAL_SENSOR_EPS) or {}
   for i, set_ep_info in pairs(set_topology_eps or {}) do
     if ib.endpoint_id == set_ep_info.endpoint_id then
       -- since EP response is being handled here, remove it from the ELECTRICAL_SENSOR_EPS table
@@ -341,7 +341,7 @@
 -- [[ DESCRIPTOR CLUSTER ATTRIBUTES ]] --
 
 function AttributeHandlers.parts_list_handler(driver, device, ib, response)
-  local tree_topology_eps = device:get_field(fields.ELECTRICAL_SENSOR_EPS)
+  local tree_topology_eps = device:get_field(fields.ELECTRICAL_SENSOR_EPS) or {}
   for i, tree_ep_info in pairs(tree_topology_eps or {}) do
     if ib.endpoint_id == tree_ep_info.endpoint_id then
       -- since EP response is being handled here, remove it from the ELECTRICAL_SENSOR_EPS table

That would avoid:

  • crashing on #nil
  • incorrectly treating non-electrical devices as candidates for power topology profiling

A more structural alternative would be to avoid routing Descriptor.PartsList / PowerTopology.AvailableEndpoints through these generic profiling handlers unless the device was already identified as having relevant electrical sensor / power topology endpoints.

Additional observations

Despite the exceptions, onboarding continues far enough that:

  • the camera profile is applied,
  • the camera sub-driver is active,
  • camera capabilities are populated.

So the bug seems non-fatal but noisy, and may still interfere with complete initialization or make onboarding flaky.

Raw log payload available

I can provide the full onboarding log if needed, but the key lines are included above.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions