Exceptions¶
anymodbus exposes a typed exception hierarchy where every class multi-inherits from the most natural standard-library or AnyIO base. Code that already catches ValueError, TimeoutError, anyio.BrokenResourceError, etc., picks up the right anymodbus exceptions without new except clauses.
Hierarchy¶
ModbusError (Exception)
├── ConfigurationError (ValueError) ← raised at construction time
├── ProtocolError (ValueError) ← raised on the wire
│ ├── ChecksumError
│ │ ├── CRCError ← RTU CRC-16 mismatch
│ │ └── LRCError ← ASCII LRC mismatch
│ ├── FrameError
│ └── UnexpectedResponseError
├── FrameTimeoutError (TimeoutError)
├── BusBusyError (anyio.BusyResourceError)
├── ConnectionLostError (anyio.BrokenResourceError)
├── BusClosedError (anyio.ClosedResourceError)
└── ModbusExceptionResponse (slave-returned exception codes 1-11)
├── IllegalFunctionError
├── IllegalDataAddressError
├── IllegalDataValueError
├── SlaveDeviceFailureError
├── AcknowledgeError
├── SlaveDeviceBusyError
├── NegativeAcknowledgeError
├── MemoryParityError
├── GatewayPathUnavailableError
└── GatewayTargetFailedToRespondError
ConfigurationError and ProtocolError both inherit ValueError, but the split is meaningful: ConfigurationError fires at construction time (bad BusConfig argument, slave address out of range, broadcast call on a unicast-only method), while ProtocolError is reserved for byte-level violations seen on the wire. Catching one independently of the other is usually what you want.
When to expect what¶
| Situation | Exception |
|---|---|
BusConfig(request_timeout=-1) |
ConfigurationError |
Slave(bus, address=999) |
ConfigurationError |
slave.read_holding_registers(...) on a broadcast handle (address 0) |
ConfigurationError |
| CRC mismatch on response (RTU) | CRCError (a ChecksumError) |
| LRC mismatch on response (ASCII) | LRCError (a ChecksumError) |
No response within request_timeout |
FrameTimeoutError |
| Slave returned exception code | ModbusExceptionResponse subclass |
| USB cable yanked mid-transaction | ConnectionLostError |
| Bus closed and another task tries to use it | BusClosedError |
| Two tasks bypass the lock and race | BusBusyError (should never normally happen) |
| Response slave-addr or FC echo wrong | UnexpectedResponseError |
Asked for count=200 registers |
ProtocolError (raised before sending) |
code_to_exception¶
For test fixtures and downstream library code that builds exception responses synthetically: