Repository navigation
ConBee II / RaspBee II fail to start since zigpy 2.4.0: no endpoint slot left for the Green Power endpoint 242 #280
Description
Problem
Since zigpy 2.4.0, ZHA no longer starts on a ConBee II (and presumably a RaspBee II) that was previously used with zigpy ≤ 2.3. Setup fails on every retry with:
Failed setup, will retry: No available endpoint slots exist: {0: SimpleDescriptor(endpoint=1, profile=260, device_type=1024, device_version=0, input_clusters=[0, 6, 10, 25, 37, 1281], output_clusters=[1, 32, 1280, 1282]), 1: SimpleDescriptor(endpoint=2, profile=49246, device_type=2080, device_version=0, input_clusters=[0], output_clusters=[])}
Reported in home-assistant/core#189366 on HA 2026.11.0b0 (zha 2.4.1, zigpy 2.4.1, zigpy-deconz 1.1.0). Practically every existing ConBee II / RaspBee II ZHA user will hit this when upgrading to 2026.11.
Cause
- zigpy 2.4.0 (zgp: add the Green Power manager, device, and proxy runtime zigpy#1848) added a third endpoint to
ControllerApplication.register_endpoints: endpoint 242 (Green Power), registered after endpoint 1 (HA) and endpoint 2 (ZLL) (zigpy/application.py:1065). - The ConBee II / RaspBee II firmware only has two
configure_endpointslots (add_endpoint fails to add endpoint 242 for Green Power #234, Endpoint count limitation still 2? dresden-elektronik/deconz-serial-protocol#22). add_endpointfinds both slots already holding zigpy's exact endpoint 1 and 2 descriptors (the two in the error above), marks both as used, and raisesValueErrorfor 242 (application.py:537). The error propagates out ofstart_network(), nothing catches it, and since nothing gets written the next retry fails identically.
Reproduced with a mocked slot table in this repo's test fixtures (origin/dev + zigpy 2.4.1):
| Slot table | Result |
|---|---|
| 2 slots already holding zigpy's endpoint 1 and 2 (any stick used with zigpy ≤ 2.3) | ValueError on every startup |
| 2 slots with other contents (fresh stick, or previously used by the deCONZ app) | No error, but 242 overwrites the ZLL endpoint 2 slot, and every startup rewrites that slot twice (2, then 242) |
| 3 slots | Works |
The second row is a separate bookkeeping bug: the write path never adds target_index to self._written_endpoints (only the "already registered" path does, application.py:519), so a later registration can pick a slot that was just written.
Who is affected
Only the contents of the two slots at the first zigpy 2.4 start matter; forming or resetting a network doesn't touch them.
- Fails: sticks last run with zigpy 2.1–2.3, whose slots hold exactly zigpy's endpoint 1 and 2 (endpoint 1 gained KeepAlive in 2.1.0). That's every existing ZHA ConBee II / RaspBee II install upgrading to HA 2026.11.
- Works (with the ZLL endpoint silently replaced by 242): a fresh stick, one previously used with deCONZ or Z2M, one last run with zigpy 2.0, or three/four-slot firmware. This is why it doesn't show up in test setups. More detail in this comment.
Firmware
Updating the firmware doesn't help today. Stable ConBee II / RaspBee II firmware (0x26780700) has two slots; a four-slot ConBee II test build from 2022 (0x26790700) never shipped; and the new ConBee II beta 0x28010700 (2026-10-09) doesn't mention endpoints in its changelog, so its slot count needs testing. RaspBee II has no beta, and ConBee III is unknown. Sources in this comment.
Proposed fix
deCONZ and zigbee2mqtt both use the two slots for endpoint 1 (HA) and endpoint 242 (GP), with no ZLL endpoint. Code references are in this comment.
- zigpy-deconz: on two-slot firmware, do the same: give slot 1 to endpoint 242 instead of the ZLL endpoint 2. Keep registering endpoint 2 where a third slot exists, and record written slots in
_written_endpoints. Nothing in zigpy, ZHA or the radio libraries sends from or relies on the coordinator's endpoint 2:send_packetcollapses every source endpoint other than 242 to 1. - zigpy (only for radios that can't host 242 at all): make the GP endpoint optional, and skip the sends that depend on it (proxy commissioning mode, pairing, GP response, the GP group subscription). Direct GPDF reception keeps working without the endpoint: the firmware's GP indications go through
api.pystraight intogp_packet_received().
Skipping 242 in add_endpoint with a warning is not safe on its own. zigpy sends from source endpoint 242 outside of GP pairing too: every permit() call goes through permit_gp() (zigpy/application.py:1850), including the permit(0) at the end of startup (zigpy/application.py:289), and that broadcasts a GP Proxy Commissioning Mode with src_ep=242 (zigpy/zgp/manager.py:616). Z2M hit exactly this: ConBee II firmware rejected the send from the unregistered endpoint with APS confirm status 0xA6 (Koenkk/zigbee2mqtt#27902; the reports were on 0x26580700 and 0x266b0700). In zigpy that becomes a DeliveryError from permit(0), which only tolerates MAC_CHANNEL_ACCESS_FAILURE, so startup would still fail on such firmware. (On 0x26780700 the send works, see puddly's hardware test in the comments, but incoming Green Power proxy frames to an unregistered 242 are dropped there.)
Fix
- Fit the Green Power endpoint into two-slot firmware #281 (draft) implements the proposed fix in zigpy-deconz. Steps to test it on Home Assistant OS are in this comment, which also covers what the ZLL endpoint 2 was originally for.
- Fail cleanly when sending while disconnected #282 (draft) fixes the
AttributeErrorbelow.
Also seen in the report
An AttributeError: 'NoneType' object has no attribute 'aps_data_request' from send_packet (application.py:579), raised while replying to an OTA Query Next Image. It looks like a side effect: an incoming frame was handled after the failed startup had already disconnected and cleared self._api. Separate from the endpoint problem, but worth a guard.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Size
Area
Activity
zigpy-review-bot commentedon Oct 28, 2026
Follow-up: firmware versions, deCONZ and Z2M
Firmware
UNSUPPORTED, which is what our detection loop relies on.add_endpointcomes from (Allowing up to 4 Endpoints #196 → Detect the number of endpoints instead of hard-coding it #203). It never made it into a release on the firmware changelog.Sending from an unregistered endpoint
Z2M hit exactly this in Koenkk/zigbee2mqtt#27902 (July 2025). zigbee-herdsman 2.5.x sent GP Proxy Commissioning Mode from endpoint 242 on permit join while 242 wasn't configured, and the firmware failed the APS-DATA.request with confirm status 0xA6. Every report there was on ConBee II firmware 0x26580700 or 0x266b0700. The changelog entry for 0x266d0700 (2021) reads "Support receiving and sending APS frames to and from endpoints which aren't registered". So on current firmware an unregistered 242 may work, but the firmware in those reports rejected it, and we can't assume every user has updated.
In zigpy, that rejection would surface as a
DeliveryErrorfrom the startuppermit(0)→permit_gp(), which only toleratesMAC_CHANNEL_ACCESS_FAILURE, so startup would still fail on such firmware. A "skip 242 with a warning" fix therefore isn't safe on its own.What deCONZ and Z2M do with the two slots
Both use slot 0 = HA endpoint 1, slot 1 = GP endpoint 242 (profile 0xA1E0, device 0x0064, client cluster 0x0021), and neither has a ZLL endpoint:
rest_configuration.cpp/backup.cppin dresden-elektronik/deconz-rest-plugin.fixParamEndpoint0/fixParamEndpoint1insrc/adapter/deconz/driver/driver.ts, written and verified on every start since fix: deCONZ: Joining bug in some setups and endpoint configuration Koenkk/zigbee-herdsman#1445. That PR was by manup, "tested with ConBee 1, 2 and 3", and fixed the 0xA6 issue above.zigpy only sends from endpoint 1 or 242 on deCONZ:
send_packetcollapses every other source endpoint to 1. Nothing in zigpy, ZHA or the radio libraries references the coordinator's endpoint 2.I've updated the proposed fix in the issue body accordingly: on two-slot firmware, give slot 1 to the GP endpoint 242 like deCONZ and Z2M, keep the ZLL endpoint 2 where a third slot exists, and fix the
_written_endpointsbookkeeping.zigpy-review-bot commentedon Oct 28, 2026
Why it works on some setups and fails on others
Whether startup fails depends only on what's in the two endpoint slots the first time zigpy 2.4 starts. Forming or resetting a network doesn't touch them (
form_network,reset_network_infoandwrite_network_infonever writeconfigure_endpoint), so a new network on an already-used stick fails the same way.{0: endpoint 1, 1: endpoint 242}, and every later startup works too, writing slot 1 twice (endpoint 2, then 242).So a test setup that started from deCONZ/Z2M or a freshly flashed stick never hits the error, while upgraded production installs always do.
My post-merge review of #279 (comment) caught the second case (the ZLL endpoint being evicted on two-slot firmware) and called it "probably harmless, but accidental". It missed the first one: I only probed slot tables that didn't already match zigpy's descriptors, not the upgrade path.
zigpy-review-bot commentedon Oct 28, 2026
Fix PRs
send_packetraises aDeliveryErrorinstead of theAttributeErrortraceback when the radio is already disconnected.What endpoint 2 was for
ControllerApplication.register_endpointswith endpoint 1 (HA) and endpoint 2 (ZLL) as defaults for every radio, which is how zigpy-deconz started writing endpoint 2. Before that, zigpy-deconz wrote no endpoints at all.send_packetsends everything except ZDO and Green Power from endpoint 1 anyway.Testing #281 on Home Assistant OS
Test build: zha-test-pr281.zip
It's the ZHA integration from HA 2026.11.0b0, unchanged except that its
manifest.jsonalso installs zigpy-deconz from the #281 branch (plus the generated English translations, so the UI isn't missing text). It's meant for 2026.11.0b0; remove it before updating Home Assistant.zhafolder from the ZIP intocustom_componentsin your config folder (the one withconfiguration.yaml; createcustom_componentsif it doesn't exist):- Without a terminal: download the ZIP, unpack it, and copy the
- With the Terminal & SSH app:
The result should bezhafolder over, e.g. with the Samba share app.custom_components/zha/manifest.json, notcustom_components/zha/zha/….zhais loaded, which is expected. HA reinstalls the zigpy-deconz test build on every start, so GitHub has to be reachable; if that install fails, ZHA won't load.zigpy_deconzand share theGot endpoint slots/Writing ... to slotlines plus the ConBee firmware version.To go back: delete
custom_components/zhaand restart. The new endpoint layout stays on the stick and works with the released version too. The zigpy-deconz test build stays installed until the next HA update replaces the container.The same files are also on the branch zigpy-review-bot/zigpy-deconz@zigpy-bot/zha-test-pr281, e.g. for
git clone --depth 1 -b zigpy-bot/zha-test-pr281 https://github.com/zigpy-review-bot/zigpy-deconz.gitand copyingcustom_components/zhaover.How the test build was made and checked
homeassistant/components/zhafrom the 2026.11.0b0 tag,translations/en.jsongenerated withpython -m script.translations develop --integration zha, and inmanifest.jsonaversion, a changednameand the extra requirementzigpy-deconz @ git+https://github.com/zigpy/zigpy-deconz.git@zigpy-bot/gp-endpoint-slots. Nothing else differs from the tag.zigpy-deconz==1.1.0and the branch build reports1.1.0.post2+git…, but HA installs and checks each manifest requirement separately, so the extra line only replaces zigpy-deconz, and nothing checks zha's own pin at runtime.git archiveofcustom_componentson that branch (SHA-256ddd8f213d0d8d19e9cbfb7cb2eb7114457cb24e885367ac13fd831029bf1bf5f).zhais picked up, the requirements install,zigpy-deconz 1.1.0.post2+git.7d9b369ewith the fix replaces 1.1.0 while zha, zha-quirks and zigpy stay at 2.4.1 / 2.4.0 / 2.4.1, and the component and config flow import. A second start only reinstalls the zigpy-deconz build. Not run against a real ConBee here.puddly commentedon Oct 28, 2026
I ran some tests on a two-device network with Ziggurat and a Conbee II...
The Conbee has the unfortunate restriction of filtering incoming packets by both profile and endpoint ID.
I think dropping ZLL is the the most practical solution here, unfortunately. A better solution would be to switch to a different coordinator 😄.
AI Summary
Tested on ConBee II (firmware
0x26780700, 2 endpoint slots): loopback, plus over the air from a second radio joined as a router.0xC05Esent to HA endpoint 1 is dropped. Clusters are not checked.permit_gp()from an unregistered 242 works.0x01040xC05E0xC05E0xA1E0With 2 slots, one of zigpy's 3 endpoints is always missing:
0xC05Eframes fail.deCONZ registers HA endpoint 1 in slot 0 and GP 242 in slot 1. It has no ZLL endpoint and sends ZLL requests from endpoint 1.
Aside: the firmware sends its own ZCL Default Response (SUCCESS) to every ZCL frame it delivers, even Read Attributes. The host then answers too.
puddly commentedon Oct 28, 2026
A third solution would be to drop GP support from the Conbee II, to avoid regressing existing users relying on ZLL packet parsing.
@zigpy-review-bot Check quirks to see if any devices rely on the coordinator being able to receive ZLL profile packets.
zigpy-review-bot commentedon Oct 28, 2026
Do any quirks or ZHA features need incoming ZLL (0xC05E) frames at the coordinator?
No. I found nothing in zha-device-handlers (dev, b9cf6214), zha or zigpy that depends on the coordinator receiving ZLL-profile packets.
Endpoint.requestin zigpy/endpoint.py. Everything else to a ZLL endpoint goes out as ZHA profile, includingget_group_identifiers(0x41), the only LightLink command ZHA sends (LightLinkGroupJoinin zha/application/platforms/virtual.py). Its reply therefore comes back as ZHA profile to endpoint 1. Neither zha nor the quirks send any touchlink commissioning command, so nothing ever waits for a ZLL-profile reply.Application.get_endpoint_idreturnsDEFAULT_ENDPOINT_ID), never endpoint 2. By your test table a 0xC05E frame sent to endpoint 1 is dropped whichever layout is used, so those devices are no worse off under either option. Whether a given remote tags its button commands with the ZLL or the ZHA profile is something only a sniffer capture of the device can answer; I haven't verified that for any of them.So I don't see a quirk-level reason to keep the ZLL endpoint on two-slot sticks. The one case I can't rule out is a third-party or unquirked device that sends an unsolicited ZLL-profile frame to the coordinator (e.g. a touchlink-style scan response), which needs a sniffer capture rather than a code search.