You send a command from Home Assistant or the Zigbee2MQTT frontend, the device does nothing, and the log shows a failed send with a MAC-layer code in it. If the code is MAC_NO_ACK, also logged as ‘MAC no ack’ or status 233, you are looking at a different failure from the better-known channel access error, and the fix is usually not the one people reach for first.
This article is aimed at people already running Zigbee2MQTT on a Z-Stack-style adapter. It covers what the code means, where it sits among its neighbours, and which checks are cheapest. Everything here stays local. Nothing in the diagnosis involves a cloud service.
The code names and numbers below are the Z-Stack ones from the zigbee-herdsman source. Ember-based adapters name their errors differently. No sourced Ember equivalent table turned up in this research, so this article does not guess at one.
What the log line looks like
The exact wording varies by Zigbee2MQTT version and by what you were trying to do. The shape is a failed publish or ZCL command to a device, followed by a data request failure and the numeric status. An illustrative example, built with a placeholder address and not copied from a real log:
error: Publish 'set' 'state' to 'hallway_plug' failed ZCL command 0x00158d0001234567/1 genOnOff.on failed Data request failed with error: 'MAC no ack' (233)
Treat this as a template for what to search for, not verbatim output, because the wording shifts between releases. Older releases logged it as “AF data request fails, status code: 233. MAC no ack”, which is still the title of a Zigbee2MQTT forum thread. Searching your log for 233 together with “MAC no ack” catches both forms.
A Home Assistant Community thread from November 2025 (Zigbee2MQTT 2.3.6-1, a Sonoff Stick P) shows the usual reply pattern for this class of failure. Posters suggest Wi-Fi interference, adapter firmware, a USB extension cable, and re-pairing after battery changes. The visible log text in that thread did not itself name MAC_NO_ACK, so it serves here only as an example of how these failures get discussed, which tends to be as one undifferentiated “interference” problem.
Why the official FAQ does not settle it
The Zigbee2MQTT FAQ has a “Common error codes” section. As checked on 2026-10-01, it explains MAC_CHANNEL_ACCESS_FAILURE (a crowded wireless spectrum, so change the Zigbee channel) and NWK_TABLE_FULL. It does not cover MAC_NO_ACK. That is a narrow gap in one section. The same FAQ has separate sections on pairing failures, on networks that are unstable or perform poorly, and on adapter crashes with timeouts such as SRSP AF dataRequest after 6000 ms, so those are worth reading alongside this.
The practical result is that a MAC_NO_ACK reader lands on the channel-access explanation and changes the channel. Sometimes that is right. For a single device failing, it often is not.
Three families of failure
The most useful move is to ask where in the path the failure happens. This grouping is the site’s own synthesis, not a Zigbee2MQTT document. It maps the Z-Stack status codes onto three stages.
| Family | Z-Stack code | Hex / decimal | What it points at |
|---|---|---|---|
| Could not transmit | MAC_CHANNEL_ACCESS_FAILURE | 0xE1 / 225 | Radio could not get a clear channel |
| Transmitted, not acknowledged (one hop) | MAC_NO_ACK | 0xE9 / 233 | First hop on the route never acknowledged the frame |
| Transmitted, not acknowledged (end to end) | APS_NO_ACK | 0xB7 / 183 | Target’s application-layer acknowledgement never came back |
| No route | NWK_NO_ROUTE | 0xCD / 205 | Network layer has no path to the target |
Two cautions on the numbers. First, these pairings come from the Z-Stack status enum in zigbee-herdsman’s source. Second, forum posts about ConBee and deCONZ adapters sometimes show a different value labelled as an APS no-ack. That belongs to another adapter family, so do not mix it into this table.
The mechanism below is general IEEE 802.15.4 behaviour: a per-hop acknowledgement and a configurable retry count (the macMaxFrameRetries attribute, which TI’s Z-Stack exposes as maxFrameRetries in its MAC settings). Mapping it onto Zigbee2MQTT’s errors is this site’s own reasoning. No Zigbee2MQTT document states it.
MAC_NO_ACK versus MAC_CHANNEL_ACCESS_FAILURE
At the MAC layer, a frame is retried a configured number of times. MAC_NO_ACK means none of those attempts was acknowledged by the next hop. The frame was sent, but nobody confirmed receiving it. MAC_CHANNEL_ACCESS_FAILURE means the radio could not find a clear channel to transmit in the first place, so there was nothing to acknowledge.
One detail changes how you read it: MAC acknowledgements are hop by hop. When the coordinator reports MAC_NO_ACK, the silent party is the first device on the route, not necessarily the device you addressed. For a device one hop from the coordinator those are the same. For a device three hops away, the frame goes to a router first, and that router is what failed to answer. A bulb switched off at the wall or an unplugged smart plug can take out a router that other devices depend on.
The FAQ ties interference to channel access failure only. This article does not claim interference causes MAC_NO_ACK. A busy channel can plausibly reduce successful exchanges in general, but the sources used here do not say so for this specific code, and the error itself describes a missing acknowledgement, not a blocked transmitter.
I think the habit worth building is reading the code before touching the channel setting. A channel change makes sense for a channel access failure. For a no-ack, it can easily send you on a detour while the real cause is one device or one router.
Candidate causes, cheapest test first
The next list is a set of candidates for “transmitted but unacknowledged”, ordered by how cheaply you can test each. It is a checklist, not a diagnosis of any one log line.
- The target, or a router in front of it, is powered off. A smart plug on a switched socket or a bulb at a dead wall switch looks just like an unreachable device, and if it was a router rather than an end device, devices behind it lose their path too. Check the physical state before the radio.
- The target is a sleepy battery device. This is a weaker match than it looks. End devices never receive unsolicited traffic: the parent holds the frame until the device polls for it, so a device that stays asleep tends to produce an expired transaction, a different 802.15.4 status, rather than a missing acknowledgement. It still costs nothing to rule out. Wake it with a button press and retry. The wake-up and configure article covers this pattern.
- The first hop is out of range, or the route is stale. If the router the coordinator sends through has moved, dropped out or been replaced, the frame has nobody to acknowledge it. The network map and LQI article and the linkquality explainer help you see which routers sit between the coordinator and the device.
- The return path is poor. Zigbee2MQTT’s own range guide frames reliability as a good round trip, command out and reply back, and not just strong transmit power. That is why a device can look close and still fail acknowledgements. The TX power article explains why raising power alone may not help.
- The adapter placement is bad. The same range guide recommends a quality adapter on a USB extension cable of about 50 cm, kept away from the computer, plus mains-powered routers to extend the mesh.
What not to do first
Changing the Zigbee channel is a heavy action. Sleeping devices only switch when they next wake, and some devices do not follow a channel change at all and need re-pairing. The channel change article walks through what that involves. For a NO_ACK from one device, the checks above are cheaper and more likely to be relevant.
Mass re-pairing is the other tempting move. The Home Assistant thread above mentions re-pairing after battery changes, which is a narrower situation. Removing and re-adding every device throws away automations and friendly names for a failure that may belong to one device.
Do not change the channel or re-pair the whole network because a single device logs MAC_NO_ACK. Both steps are disruptive, and no source in this research presents either as the fix for this specific code.
When it points at something bigger
If several devices fail at once, or the same device fails along with unrelated ones, the individual-device checks stop being useful. At that point the candidates shift to the adapter, its power supply, its firmware, or a stalled stack. The FAQ’s adapter crash and timeout section is the better starting point, and the many-to-one route failure article covers the neighbouring routing failures on Ember adapters. If the failure appears during setup rather than normal use, the failed to configure article is closer to the symptom.
FAQ
What does MAC_NO_ACK mean in Zigbee2MQTT?
On a Z-Stack adapter it is status 0xE9 (233), logged as ‘MAC no ack’. In general 802.15.4 terms it means a frame was sent but the first hop on the route never acknowledged it after the allowed retries. That hop is the target only if the target is a direct neighbour of the coordinator. The mapping to Zigbee2MQTT’s error output is this site’s synthesis, not an official definition.
What is the difference between MAC_NO_ACK and MAC_CHANNEL_ACCESS_FAILURE?
MAC_CHANNEL_ACCESS_FAILURE (0xE1, 225) means the radio could not get a clear channel to transmit. MAC_NO_ACK (0xE9, 233) means it did transmit and received no acknowledgement. The Zigbee2MQTT FAQ attributes interference to the first only.
Why does Zigbee2MQTT say the data request failed for a device that is online?
Zigbee2MQTT’s availability state governs the online/offline badge, based on when it last heard from or pinged the device. It does not test whether a command can get through right now. A device still marked online can sit behind a router that has since gone away, and the next command to it fails.
Will changing the Zigbee channel fix MAC_NO_ACK?
Not reliably. The FAQ recommends a channel change for MAC_CHANNEL_ACCESS_FAILURE, and does not mention it for MAC_NO_ACK. Test the device, its parent router, and the return path first.
Do battery devices cause MAC_NO_ACK errors?
Less often than you might expect. A sleeping end device’s parent holds the frame until the device polls for it, so a device that never wakes in time usually surfaces as an expired transaction rather than a no-ack. Waking the device and retrying is still a free test.
What this research checked
This article is research synthesis. The sources are the Zigbee2MQTT FAQ’s error code section and range guide, the Z-Stack status constants in zigbee-herdsman, general IEEE 802.15.4 MAC behaviour, and two community threads. No MAC_NO_ACK failure was reproduced, and no test bench data supports any claim here.
The sources confirm the Z-Stack code pairings and that the FAQ’s “Common error codes” section does not cover MAC_NO_ACK. The three-family framing and the cause checklist are this site’s own reasoning. Ember-side naming is not covered. After any change, check the failing device again and watch whether the same code returns for that device alone or for others too.