Why Modbus exceptions matter
Modbus is a polite protocol. When a slave can't satisfy a request, it doesn't drop the connection or stay silent - it answers, but with the high bit of the function code set, and a single byte of exception information. That byte is the most diagnostic information you'll ever get out of a misbehaving Modbus device. Read it correctly and most "the meter is broken" support cases resolve in under five minutes.
The ten codes are deceptively similar. Three of them (02, 03, and 04) account for roughly 80% of the exceptions you'll see in the field. The rest are rare but informative. This post covers all of them, in order, and applies equally to Modbus RTU (serial) and Modbus TCP - the exception layer is identical above the framing.
Modbus exception codes at a glance
| Code | Name | Typical cause | Where to look first |
|---|---|---|---|
01 | Illegal Function | Slave doesn't implement this function code | Device's protocol manual |
02 | Illegal Data Address | Address (or range) doesn't exist on this device | Addressing base, register map, polling quantity |
03 | Illegal Data Value | A value in the request frame is illegal at the protocol level | Quantity field, coil value byte, byte-count mismatch |
04 | Slave Device Failure | Internal failure on the device | Device event log, vendor support |
05 | Acknowledge | Slave accepted, working on it (poll back later) | Long-write / programming operations on legacy controllers |
06 | Slave Busy | Slave is processing a long command | Poll cadence, master FB busy bit |
07 | Negative Acknowledge | Slave can't perform this programming function | Legacy Modicon controllers; vendor or replace |
08 | Memory Parity Error | Slave detected internal memory corruption | Replace the device |
0A | Gateway Path Unavailable | Gateway can't route to the downstream slave | Gateway routing table, serial port config |
0B | Gateway Target Failed to Respond | Gateway reached the bus but the slave is silent | Unit ID, slave power, bus addressing |
(09 is not assigned in the Modbus Application Protocol spec - the codes jump from 08 to 0A.)
Anatomy of a Modbus exception response
An exception response is three bytes of payload, framed normally:
Unit ID FC | 0x80 ExceptionCode CRC
─────────────────────────────────────────
0x11 0x83 0x02 0x...
▲ ▲ ▲
│ │ └── 02 = Illegal Data Address
│ └── 0x83 = 0x03 (Read Holding) with high bit set
└── slave 17
The function code echoed back is your original function code with its high bit (bit 7) set - i.e. OR'd with 0x80. Normal request function codes are defined in the range 0x01-0x7F, so their high bit is always 0; a response byte with the high bit set is always an exception, never a real function code. A failing 0x03 (Read Holding Registers) comes back as 0x83, a failing 0x10 (Write Multiple Registers) becomes 0x90, and so on. The exception byte that follows tells you the specific failure.
In practice, almost nobody sees that high bit the first time something goes wrong. What you see is whatever your PLC's Modbus block or SCADA driver chose to surface: a bError boolean and an nErrId word on Beckhoff/TwinCAT (FB_MBReadRegs and friends); a multi-word management parameter on Schneider M580 READ_VAR / WRITE_VAR where the operation report sits in the high byte of the second word and the Modbus exception falls out of that; a populated .ERR and .EXERR on a Rockwell MSG instruction; or a bad-quality tag with a "Modbus exception 02" string in Ignition or Kepware. The exception code is in there - it's just one or two layers up from the wire.
When the function block's error output isn't enough to pin down a cause, drop down to a tool that captures the actual exchange. Wireshark covers Modbus TCP; Modbus Poll is the classic ad-hoc test master; the Coilware Modbus traffic tool captures both serial and TCP in one place. The bytes don't lie - the driver above them sometimes does.
One special case: requests sent to unit ID 0x00 are broadcasts, and slaves must never respond to them - including with exceptions. If you broadcast a malformed write and hear nothing back, that's spec-correct behavior, not a fault.
Modbus exception code 01 - Illegal Function
The function code you sent is not implemented on this slave. In practice this is almost never "the slave doesn't speak Modbus" - it's "the slave doesn't implement this specific function". The most common offender is 0x17 (Read/Write Multiple Registers) on small embedded slaves that only implement 0x03 (Read Holding Registers), 0x06 (Write Single Register), and 0x10 (Write Multiple Registers).
How to fix. Check the device's protocol manual for the supported function codes. Fall back to a function code the device implements. If you're polling register pairs as one transaction, split the write into a separate 0x06 or 0x10 request.
Modbus exception code 02 - Illegal Data Address
The most common exception in the field, by a wide margin. The slave is telling you that the register address you asked for doesn't exist on this device. This is almost always one of two things:
- You're using the wrong addressing base - the device documents registers in the Modicon
4xxxxconvention (1-based, e.g.40001) but the protocol uses 0-based addresses on the wire. - You're polling beyond the end of a register block. The slave allows you to start at a valid address but the quantity walks off the end.
Worth knowing: the slave validates the entire range of the request, not just the starting address. If you issue a 0x03 (Read Holding Registers) for 8 registers starting at 3023, all eight addresses (3023 through 3030) have to be implemented on the device. A single hole anywhere in that range produces one 02 and you get no data back at all - not a partial read.
How to fix. In Coilware Modbus, run the Scan tool's register sweep against a window either side of your target - it classifies each address as OK, illegal, or exception, so you can see exactly where the device's real map starts.
Modbus exception code 03 - Illegal Data Value
A value in the request frame itself is illegal at the protocol level. The Modbus spec is explicit on this: 03 does not mean a data value is outside the application's expectations - the protocol has no idea what any particular register actually represents. "I wrote 500 to a register that only accepts 0-100" is an application-range violation, not what 03 is strictly for.
Spec-compliant causes are about the frame, not the meaning: a quantity field out of the allowed range (zero registers, or more than the PDU cap of ~125 in a 0x03 / Read Holding, ~123 in a 0x10 / Write Multiple); a coil write (0x05, Write Single Coil) with a value other than 0x0000 or 0xFF00; a byte-count field that doesn't match the quantity field in 0x10.
In practice, plenty of slaves bend the spec and return 03 for application-range violations anyway, while others return 02 or 04 for the same case. Writes to a read-only register are similarly vendor-dependent - some return 03, most return 02, a few return 04. Don't anchor on the code alone to diagnose value problems.
Modbus exception code 04 - Slave Device Failure
The slave tried, and failed, to satisfy a valid request. This is the device's way of saying "something is wrong on my end, not yours". Treat it as a device failure and contact the vendor.
Field tip. Whenever you raise a support case with a vendor about any exception - 04 especially - attach a frame capture of the request and the response. Wireshark works for Modbus TCP; the Coilware Modbus traffic tool captures both serial and TCP. Vendor support engineers triage cases an order of magnitude faster when they can see the actual bytes on the wire instead of working from "it doesn't work" prose.
Modbus exception code 05 - Acknowledge
"Accepted, working on it." The slave has acknowledged the request and is processing it, but the operation will take longer than a normal response window - the master is expected to poll back later (historically with a Poll Program Complete request). Rare; mostly seen during flash writes or programming operations on legacy controllers. If you're not running a long-write transaction and you see this, you probably have a misbehaving gateway translating something it shouldn't.
Modbus exception code 06 - Slave Busy
The slave is processing a long-running command and cannot accept a new one. Retry with backoff. If you see this constantly, your poll cadence is faster than the slave's processing budget - slow down, or split the read into smaller chunks.
Field tip. Most master-side Modbus function blocks already expose a "busy" or "active" output while a transaction is in flight - bBUSY on Beckhoff/TwinCAT (FB_MBReadRegs and the rest of the family), the ACTIVE bit on Schneider M580 READ_VAR / WRITE_VAR, and equivalent "command in progress" status words on ProSoft modules. Gate your next request on that bit going low rather than firing on a fixed timer. You'll avoid most 06s by simply not asking faster than the slave - or your own stack - can answer.
Modbus exception code 07 - Negative Acknowledge
Almost never seen outside legacy Modicon controllers. The slave understood your function code but cannot perform the requested action - historically returned by programming functions (0x0D / 0x0E, the program/poll controller pair) when the device wasn't in a state that allowed them. If a modern field device returns 07, treat it as a slave-side bug: capture the frame, contact the vendor, and replace the device if the behavior persists after a firmware update.
Modbus exception code 08 - Memory Parity Error
Very rare. The slave detected a parity error reading its own memory. Usually indicates actual hardware degradation. Replace the device.
Modbus exception code 0A - Gateway Path Unavailable
Returned only by Modbus gateways (TCP-to-RTU bridges, usually). The gateway can't allocate a path from your input port to the downstream slave. This is just as often a configuration problem as a physical one: the unit ID you addressed isn't mapped in the gateway's slave list, the gateway's serial port is set to the wrong baud rate or parity for the downstream bus, or the gateway is overloaded and out of internal handles. Check the gateway's routing table and serial port config first; check the cable, termination, and status LEDs second.
Modbus exception code 0B - Gateway Target Failed to Respond
The gateway reached the serial network but the specific slave you addressed didn't respond. Different cause from 0A: the bus is live but this slave is silent. Check unit ID, check bus addressing, check whether the slave is powered.
Frequently asked questions
What does Modbus exception code 02 mean?
02 (Illegal Data Address) means the register address you requested - or one of the addresses inside the range you requested - doesn't exist on the device. The slave validates the whole (start + quantity) range, so a single hole in a multi-register read produces one 02 and no data. The two usual culprits are an off-by-one mistake between the Modicon 4xxxx documentation and the 0-based wire address, and a quantity field that walks past the end of a register block.
How do I fix an illegal data address error?
Verify your addressing base first: if the device's manual lists 40001 and you wrote that on the wire, drop it by one to 0x0000. Then check that every register in your requested range is actually implemented - many devices have sparse maps with documented holes. A register sweep across the range (offered by tools like Coilware Modbus and Modbus Poll) tells you exactly which addresses respond and which throw 02.
Are Modbus exception codes the same in Modbus RTU and Modbus TCP?
Yes. The exception layer (response function code with the high bit set, plus a one-byte exception code) is identical in both. The only difference is the framing around it - Modbus RTU wraps the response in a slave address and CRC, Modbus TCP wraps it in the MBAP header. The exception codes themselves and what they mean are the same.
Why is my Modbus master reporting "timeout" when the slave is actually responding?
The slave is probably returning an exception, and your master-side driver is choosing to surface it as a generic timeout rather than the actual code. This is common on older client libraries and on PLC function blocks that don't expose the full exception byte. Capture the exchange on the wire (Wireshark for TCP, Modbus Poll or Coilware Modbus for serial) - if you see a response with a function code byte ≥ 0x80, your driver is hiding the exception from you.
Troubleshooting flowchart for Modbus exceptions
When a Modbus function block lights up its error bit or a SCADA tag goes bad-quality, work through the following in order:
Wrap-up
Exceptions are the most useful diagnostic information Modbus gives you. Read them carefully, capture them on the wire, and most of your field cases resolve before lunch.
We're writing the Modbus Debugging Field Manual right now - a free ebook covering every exception in much more depth, with worked examples from real plant work and the byte-order, gateway, and timing traps that don't fit in a blog post. It's not out yet. Join the waitlist and we'll email you the day it ships.