Matter non-bridge mode produces an unreachable endpoint tree (root PartsList omits endpoint 1)
PROBLEM DESCRIPTION
When Matter bridge mode is disabled, the Root Node endpoint's Descriptor.PartsList (0/0x001D/0x0003)
omits the aggregator at endpoint 1 — but endpoint 1 is still instantiated, still reports device type
0x000E (Aggregator), and its own PartsList still claims the application endpoints. The result is an
endpoint tree in which the app endpoints are only reachable through an endpoint that the root never
announces.
Per the Matter Core Specification (Descriptor cluster, PartsList), the Root Node endpoint's PartsList
is a flat "full-family" list that must enumerate every endpoint on the node except endpoint 0. A
controller is entitled to read 0/0x001D/0x0003 and treat it as the complete endpoint inventory.
Controllers that build a real endpoint hierarchy therefore lose the whole subtree: endpoint 1 is claimed by nobody, so it is never attached, and the app endpoints it claims go with it. The device shows up with endpoint 0 only and cannot be read or controlled. Controllers that merely keep a flat attribute map (Home Assistant's python-matter-server) never notice, which is why this has gone unreported.
What the device reports
Read from a real device (Tasmota 15.6.0.1, ESP32-U4WDH-D v3.1, VID 0xFFF1 / PID 0x8000,
two On/Off Plug-in Units), decoded from the raw TLV of a wildcard read:
| Endpoint | DeviceTypeList | PartsList |
|---|---|---|
| 0 | 0x0016 Root Node, rev 3 |
[2, 3] |
| 1 | 0x000E Aggregator, rev 1 |
[2, 3] |
| 2 | 0x010A On/Off Plug-in Unit, rev 3 |
[] |
| 3 | 0x010A On/Off Plug-in Unit, rev 3 |
[] |
Raw bytes for 0/0x001D/0x0003:
2402 00 endpoint 0
2403 1d cluster 0x001D (Descriptor)
2404 03 attribute 0x0003 (PartsList)
18 end of path
3602 array, context tag 2 (Data)
04 02 unsigned 2
04 03 unsigned 3
18 18 18 endEndpoint 1 exists and answers the read. Nothing in the node claims it.
Where it comes from
lib/libesp32/berry_matter/src/embedded/Matter_Plugin_1_Root.be (current development):
elif attribute == 0x0003 # ---------- PartsList / list[endpoint-no]----------
var pl = TLV.Matter_TLV_array()
var eps = self.device.get_active_endpoints(true)
var disable_bridge_mode = self.device.disable_bridge_mode
for ep: eps
# if bridge mode is disabled, don't announce Aggregatore (above 0xFF00)
if !disable_bridge_mode || ep != 0x0001 #-matter.AGGREGATOR_ENDPOINT-#
pl.add_TLV(nil, 0x05 #-TLV.U2-#, ep) # add each endpoint
end
end
return plThe aggregator is filtered out of the root's list, but nothing stops it from being created or from
claiming endpoints 2 and 3 in Matter_Plugin_1_Aggregator.be. (Side note: the comment still says
"above 0xFF00" while the check is ep != 0x0001, so the filter looks older than the move of the
aggregator to endpoint 1 in #20654.)
Suggested fix
Either would resolve it:
- Announce the aggregator. Drop the filter so the root
PartsListis[1, 2, 3]. Hiding endpoint 1 from the root list does not hide it from the network — a controller reading endpoint 1 directly still finds an Aggregator — so the filter buys nothing while breaking tree construction. - Don't create the aggregator when bridge mode is disabled. Root
PartsListbecomes[2, 3]and the app endpoints hang directly off the root, which is what the reported list already implies. This is the cleaner shape if the intent is "this device is not a bridge".
A smaller, unrelated observation while looking at this: endpoints 2 and 3 expose cluster 0x0039
(Bridged Device Basic Information) without carrying the 0x0013 Bridged Node device type. That cluster is
only meant to appear on a Bridged Node endpoint. It is not the cause of this problem, but it may be
worth tidying in the same area.
REQUESTED INFORMATION
- Read the Contributing Guide and Policy and the Code of Conduct
- Searched the problem in issues
- Searched the problem in discussions
- Searched the problem in the docs
- Searched the problem in the chat
- Problem is not scripter related, in this case open a discussion and tag gemu2015
- Device used (e.g., Sonoff Basic): _ESP32 (
ESP32-U4WDH-D v3.1) - Tasmota binary firmware version number used: 15.6.0.1
- Pre-compiled
- Self-compiled
- Flashing tools used:
- Provide the output of command:
Backlog Template; Module; GPIO 255:
Configuration output here:
N/A
- If using rules, provide the output of this command:
Backlog Rule1; Rule2; Rule3:
Rules output here:
N/A
- Provide the output of this command:
Status 0:
STATUS 0 output here:
N/A
- Set
weblogto 4 and then, when you experience your issue, provide the output of the Console log:
Console output here:
N/A
TO REPRODUCE
- Configure Matter on an ESP32 with two On/Off endpoints and bridge mode disabled.
- Commission the device with a controller that builds a real endpoint hierarchy from the Descriptor cluster (matter.js-based controllers do; python-matter-server does not).
- Read
0/0x001D/0x0003— it returns[2, 3], omitting the aggregator at endpoint 1. - Observe that the controller ends up with endpoint 0 only, and endpoints 2 and 3 cannot be read or commanded.
Enabling bridge mode on the same device makes the root PartsList include endpoint 1, and everything
then works.
EXPECTED BEHAVIOUR
The root endpoint's PartsList enumerates every endpoint on the node, so that a controller reading
0/0x001D/0x0003 obtains the complete endpoint inventory and can build the endpoint tree — in bridge
mode and non-bridge mode alike.
SCREENSHOTS
N/A — the relevant evidence is the decoded Descriptor tree above.
ADDITIONAL CONTEXT
Originally reported by a user of a matter.js-based controller, where the device appeared with endpoint 0
and nothing else. The controller-side handling of this non-compliant tree is being tracked separately
against matter.js: https://github.com/matter-js/matter.js/issues/4405
This issue is about the device-side PartsList.
Source: arendst/Tasmota