From d52fd58d82e3ec094377b22a1c9daaeeb3f25a2c Mon Sep 17 00:00:00 2001 From: zigpy-review-bot <286747149+zigpy-review-bot@users.noreply.github.com> Date: Fri, 30 Oct 2026 18:15:04 +0100 Subject: [PATCH] Rediscover the route and retry once on `APS_NO_ACK` in `send_packet` A stale next hop can MAC-ACK frames without forwarding them, and Z-Stack reports `APS_NO_ACK` instead of `NWK_NO_ROUTE` in that case, so the route was never repaired. --- tests/application/test_requests.py | 7 +++++-- zigpy_znp/zigbee/application.py | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/application/test_requests.py b/tests/application/test_requests.py index f073cf6..7400e50 100644 --- a/tests/application/test_requests.py +++ b/tests/application/test_requests.py @@ -534,7 +534,10 @@ def set_route_discovered(req): @pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1]) -async def test_request_recovery_route_rediscovery_af(device, make_application, mocker): +@pytest.mark.parametrize("failure", [t.Status.NWK_NO_ROUTE, t.Status.APS_NO_ACK]) +async def test_request_recovery_route_rediscovery_af( + device, failure, make_application, mocker +): app, znp_server = make_application(server_cls=device) await app.startup(auto_form=False) @@ -552,7 +555,7 @@ def data_confirm_replier(req): nonlocal route_discovered return c.AF.DataConfirm.Callback( - Status=t.Status.SUCCESS if route_discovered else t.Status.NWK_NO_ROUTE, + Status=t.Status.SUCCESS if route_discovered else failure, Endpoint=1, TSN=1, ) diff --git a/zigpy_znp/zigbee/application.py b/zigpy_znp/zigbee/application.py index 30f33e4..44bd410 100644 --- a/zigpy_znp/zigbee/application.py +++ b/zigpy_znp/zigbee/application.py @@ -986,9 +986,12 @@ async def send_packet(self, packet: zigpy.types.ZigbeePacket) -> None: continue # Perform route discovery explicitly if the stack fails and - # then retry the request + # then retry the request. `APS_NO_ACK` is included because a + # stale next hop can still MAC-ACK without forwarding, and the + # stack reports no `NWK_NO_ROUTE` in that case. if ( - e.response.Status == t.Status.NWK_NO_ROUTE + e.response.Status + in (t.Status.NWK_NO_ROUTE, t.Status.APS_NO_ACK) and device is not None and retry_attempt < 1 ):