Repository: pymodbus-dev/pymodbus

Issue:
Server not handling garbage byte after frame
Thanks for creating and maintaining this package.  

We are using a `ModbusSerialServer` with `Python 3.11` and `pymodbus==3.9.2`.  

One of the connected clients is throwing a stray `0x0` at the end of messages occasionally.  We suspect this is happening when it resets, but this is out of our control.  The server does not deal with this effectively and an exception in throw.

```
2025-05-08 21:36:01.758 |              pymodbus.logging: 329| [  DEBUG] recv: 0xfe 0x4 0x0 0x3 0x0 0x1 0xd5 0xc5 0x0 old_data:  addr=None
2025-05-08 21:36:01.758 |              pymodbus.logging:  91| [  DEBUG] Processing: 0xfe 0x4 0x0 0x3 0x0 0x1 0xd5 0xc5 0x0
2025-05-08 21:36:01.758 |                       asyncio:  74| [  ERROR] Exception in callback <bound method SerialTransport.intern_read_ready of <pymodbus.transport.serialtransport.SerialTransport object at 0x7faf0995d0>>
handle: <Handle SerialTransport.intern_read_ready>
Traceback (most recent call last):
  File "uvloop/cbhandles.pyx", line 61, in uvloop.loop.Handle._run
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/transport/serialtransport.py", line 134, in intern_read_ready
    self.intern_protocol.data_received(data)  # type: ignore[attr-defined]
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/transport/transport.py", line 304, in data_received
    self.datagram_received(data, None)
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/transport/transport.py", line 338, in datagram_received
    cut = self.callback_data(self.recv_buffer, addr=addr)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/server/requesthandler.py", line 79, in callback_data
    used_len = super().callback_data(data, addr)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/transaction/transaction.py", line 217, in callback_data
    used_len, pdu = self.framer.processIncomingFrame(self.trace_packet(False, data))
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/framer/base.py", line 76, in processIncomingFrame
    data_len, pdu = self._processIncomingFrame(data[used_len:])
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/framer/base.py", line 97, in _processIncomingFrame
    if (result := self.decoder.decode(frame_data)) is None:
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/pdu/decoders.py", line 112, in decode
    pdu.decode(frame[1:])
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/pdu/register_message.py", line 26, in decode
    self.address, self.count = struct.unpack(">HH", data)
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^
struct.error: unpack requires a buffer of 4 bytes
```

The "bad" message is not discarded and when the client starts polling again the buffer keeps filling up and the problem compounds to the point where the server is constantly throwing this exception as well as `Frame check failed` warnings and it has to be restarted.

```
2025-05-08 21:36:04.389 |              pymodbus.logging: 329| [  DEBUG] recv: 0xfe 0x4 0x0 0x19 0x0 0x1 0xf4 0x2 old_data: 0xfe 0x4 0x0 0x3 0x0 0x1 0xd5 0xc5 0x0 addr=None
2025-05-08 21:36:04.389 |              pymodbus.logging:  91| [  DEBUG] Processing: 0xfe 0x4 0x0 0x3 0x0 0x1 0xd5 0xc5 0x0 0xfe 0x4 0x0 0x19 0x0 0x1 0xf4 0x2
2025-05-08 21:36:04.389 |              pymodbus.logging: 123| [  DEBUG] Frame check failed, possible garbage after frame, testing..
2025-05-08 21:36:04.389 |              pymodbus.logging: 123| [  DEBUG] Frame check failed, possible garbage after frame, testing..
2025-05-08 21:36:04.389 |              pymodbus.logging: 123| [  DEBUG] Frame check failed, possible garbage after frame, testing..
2025-05-08 21:36:04.390 |              pymodbus.logging: 123| [  DEBUG] Frame check failed, possible garbage after frame, testing..
2025-05-08 21:36:04.390 |              pymodbus.logging: 123| [  DEBUG] Frame check failed, possible garbage after frame, testing..
2025-05-08 21:36:04.390 |              pymodbus.logging: 123| [  DEBUG] Frame check failed, possible garbage after frame, testing..
2025-05-08 21:36:04.390 |              pymodbus.logging: 123| [  DEBUG] Frame check failed, possible garbage after frame, testing..
...
...
2025-05-08 21:36:07.889 |              pymodbus.logging: 329| [  DEBUG] recv: 0xfe 0x4 0x0 0x3 0x0 0x1 0xd5 0xc5 old_data: 0xfe 0x4 0x0 0x3 0x0 0x1 0xd5 0xc5 0x0 0xfe 0x4 0x0 0x19 0x0 0x1 0xf4 0x2 0xfe 0x4 0x0 0x19 0x0 0x1 0xf4 0x2 0xfe 0x4 0x0 0x19 0x0 0x1 0xf4 0x2 0xfe 0x4 0x0 0x19 0x0 0x1 0xf4 0x2 0xfe 0x4 0x0 0x19 0x0 0x1 0xf4 0x2 0xfe 0x4 0x0 0x19 0x0 0x1 0xf4 0x2 0xfe 0x4 0x0 0x3 0x0 0x1 0xd5 0xc5 addr=None
2025-05-08 21:36:07.889 |              pymodbus.logging:  91| [  DEBUG] Processing: 0xfe 0x4 0x0 0x3 0x0 0x1 0xd5 0xc5 0x0 0xfe 0x4 0x0 0x19 0x0 0x1 0xf4 0x2 0xfe 0x4 0x0 0x19 0x0 0x1 0xf4 0x2 0xfe 0x4 0x0 0x19 0x0 0x1 0xf4 0x2 0xfe 0x4 0x0 0x19 0x0 0x1 0xf4 0x2 0xfe 0x4 0x0 0x19 0x0 0x1 0xf4 0x2 0xfe 0x4 0x0 0x19 0x0 0x1 0xf4 0x2 0xfe 0x4 0x0 0x3 0x0 0x1 0xd5 0xc5 0xfe 0x4 0x0 0x3 0x0 0x1 0xd5 0xc5
2025-05-08 21:36:07.889 |              pymodbus.logging: 123| [  DEBUG] Frame check failed, possible garbage after frame, testing..
...
... < many more Frame check failed messages >
...
2025-05-08 21:36:07.896 |              pymodbus.logging: 123| [  DEBUG] Frame check failed, possible garbage after frame, testing..
2025-05-08 21:36:07.896 |                       asyncio:  74| [  ERROR] Exception in callback <bound method SerialTransport.intern_read_ready of <pymodbus.transport.serialtransport.SerialTransport object at 0x7faf0995d0>>
handle: <Handle SerialTransport.intern_read_ready>
Traceback (most recent call last):
  File "uvloop/cbhandles.pyx", line 61, in uvloop.loop.Handle._run
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/transport/serialtransport.py", line 134, in intern_read_ready
    self.intern_protocol.data_received(data)  # type: ignore[attr-defined]
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/transport/transport.py", line 304, in data_received
    self.datagram_received(data, None)
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/transport/transport.py", line 338, in datagram_received
    cut = self.callback_data(self.recv_buffer, addr=addr)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/server/requesthandler.py", line 79, in callback_data
    used_len = super().callback_data(data, addr)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/transaction/transaction.py", line 217, in callback_data
    used_len, pdu = self.framer.processIncomingFrame(self.trace_packet(False, data))
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/framer/base.py", line 76, in processIncomingFrame
    data_len, pdu = self._processIncomingFrame(data[used_len:])
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/framer/base.py", line 97, in _processIncomingFrame
    if (result := self.decoder.decode(frame_data)) is None:
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/pdu/decoders.py", line 112, in decode
    pdu.decode(frame[1:])
  File "/home/bamboo/peta-async/.tox/py/lib/python3.11/site-packages/pymodbus/pdu/register_message.py", line 26, in decode
    self.address, self.count = struct.unpack(">HH", data)
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^
struct.error: unpack requires a buffer of 4 bytes
```


Discussion:
Sounds like a bug, the frame with the 0x0 should be discarded, let me have a look.
Side question.   Where is the best place to add a unit test for this such that raw bytes can be injected into the framer and pdu?  I saw separate tests for each, but could not determine if there was an integration of both somewhere which is more inclusive.   It was hoping to improve diagnosis and contribute in the future.  Thanks.
side response: in the test framework

Framer is responsible for delivering a pdu, so bad bytes are part of the framer.
Verified bug on v3.9.2 and on dev.




Patch:
diff --git a/pymodbus/pdu/bit_message.py b/pymodbus/pdu/bit_message.py
index 6579fbe23..a82f3d1e3 100644
--- a/pymodbus/pdu/bit_message.py
+++ b/pymodbus/pdu/bit_message.py
@@ -23,7 +23,7 @@ def encode(self) -> bytes:
 
     def decode(self, data: bytes) -> None:
         """Decode a request pdu."""
-        self.address, self.count = struct.unpack(">HH", data)
+        self.address, self.count = struct.unpack(">HH", data[:4])
 
     def get_response_pdu_size(self) -> int:
         """Get response pdu size.
@@ -84,7 +84,7 @@ def encode(self) -> bytes:
 
     def decode(self, data: bytes) -> None:
         """Decode a write coil request."""
-        self.address, value = struct.unpack(">HH", data)
+        self.address, value = struct.unpack(">HH", data[:4])
         self.bits = [bool(value)]
 
 
@@ -157,4 +157,4 @@ def encode(self) -> bytes:
 
     def decode(self, data: bytes) -> None:
         """Decode a write coils response."""
-        self.address, self.count = struct.unpack(">HH", data)
+        self.address, self.count = struct.unpack(">HH", data[:4])
diff --git a/pymodbus/pdu/diag_message.py b/pymodbus/pdu/diag_message.py
index bed347853..3dd603f8f 100644
--- a/pymodbus/pdu/diag_message.py
+++ b/pymodbus/pdu/diag_message.py
@@ -54,9 +54,9 @@ def decode(self, data: bytes) -> None:
                 data_len += 1
                 data += b"0"
             if (word_len := data_len // 2) == 1:
-                (self.message,) = struct.unpack(">H", data)
+                (self.message,) = struct.unpack(">H", data[:2])
             else:
-                self.message = struct.unpack(">" + "H" * word_len, data)
+                self.message = struct.unpack(">" + "H" * word_len, data[:2 * word_len])
 
     def get_response_pdu_size(self) -> int:
         """Get response pdu size.
diff --git a/pymodbus/pdu/file_message.py b/pymodbus/pdu/file_message.py
index 459e8baa8..27ea7bee4 100644
--- a/pymodbus/pdu/file_message.py
+++ b/pymodbus/pdu/file_message.py
@@ -235,7 +235,7 @@ def encode(self) -> bytes:
 
     def decode(self, data: bytes) -> None:
         """Decode the incoming request."""
-        self.address = struct.unpack(">H", data)[0]
+        self.address = struct.unpack(">H", data[:2])[0]
 
     async def update_datastore(self, _context: ModbusDeviceContext) -> ModbusPDU:
         """Run a read exception status request against the store."""
diff --git a/pymodbus/pdu/mei_message.py b/pymodbus/pdu/mei_message.py
index 6c8b6a49e..455aaba26 100644
--- a/pymodbus/pdu/mei_message.py
+++ b/pymodbus/pdu/mei_message.py
@@ -49,7 +49,7 @@ def encode(self) -> bytes:
 
     def decode(self, data: bytes) -> None:
         """Decode data part of the message."""
-        self.sub_function_code, self.read_code, self.object_id = struct.unpack(">BBB", data)
+        self.sub_function_code, self.read_code, self.object_id = struct.unpack(">BBB", data[:3])
 
     async def update_datastore(self, _context: ModbusDeviceContext) -> ModbusPDU:
         """Run a read exception status request against the store."""
diff --git a/pymodbus/pdu/other_message.py b/pymodbus/pdu/other_message.py
index ae973c9f6..cd58cb1f4 100644
--- a/pymodbus/pdu/other_message.py
+++ b/pymodbus/pdu/other_message.py
@@ -81,7 +81,7 @@ def encode(self) -> bytes:
 
     def decode(self, data: bytes) -> None:
         """Decode a the response."""
-        ready, self.count = struct.unpack(">HH", data)
+        ready, self.count = struct.unpack(">HH", data[:4])
         self.status = ready == ModbusStatus.READY
 
 
diff --git a/pymodbus/pdu/register_message.py b/pymodbus/pdu/register_message.py
index aa199acca..1bb3da059 100644
--- a/pymodbus/pdu/register_message.py
+++ b/pymodbus/pdu/register_message.py
@@ -1,4 +1,5 @@
 """Register Reading Request/Response."""
+
 from __future__ import annotations
 
 import struct
@@ -24,7 +25,7 @@ def encode(self) -> bytes:
 
     def decode(self, data: bytes) -> None:
         """Decode a register request packet."""
-        self.address, self.count = struct.unpack(">HH", data)
+        self.address, self.count = struct.unpack(">HH", data[:4])
 
     def get_response_pdu_size(self) -> int:
         """Get response pdu size.
@@ -38,8 +39,16 @@ async def update_datastore(self, context: ModbusDeviceContext) -> ModbusPDU:
         values = await context.async_getValues(
             self.function_code, self.address, self.count
         )
-        response_class = (ReadHoldingRegistersResponse if self.function_code == 3 else ReadInputRegistersResponse)
-        return response_class(registers=cast(list[int], values), dev_id=self.dev_id, transaction_id=self.transaction_id)
+        response_class = (
+            ReadHoldingRegistersResponse
+            if self.function_code == 3
+            else ReadInputRegistersResponse
+        )
+        return response_class(
+            registers=cast(list[int], values),
+            dev_id=self.dev_id,
+            transaction_id=self.transaction_id,
+        )
 
 
 class ReadHoldingRegistersResponse(ModbusPDU):
@@ -59,7 +68,9 @@ def decode(self, data: bytes) -> None:
         """Decode a register response packet."""
         self.registers = []
         if (data_len := int(data[0])) >= len(data):
-            raise ModbusIOException(f"byte_count {data_len} > length of packet {len(data)}")
+            raise ModbusIOException(
+                f"byte_count {data_len} > length of packet {len(data)}"
+            )
         for i in range(1, data_len, 2):
             self.registers.append(struct.unpack(">H", data[i : i + 2])[0])
 
@@ -82,13 +93,15 @@ class ReadWriteMultipleRegistersRequest(ModbusPDU):
     function_code = 23
     rtu_byte_count_pos = 10
 
-    def __init__(self,
-            read_address: int = 0x00,
-            read_count: int = 0,
-            write_address: int = 0x00,
-            write_registers: list[int] | None = None,
-            dev_id: int = 1,
-            transaction_id: int = 0) -> None:
+    def __init__(
+        self,
+        read_address: int = 0x00,
+        read_count: int = 0,
+        write_address: int = 0x00,
+        write_registers: list[int] | None = None,
+        dev_id: int = 1,
+        transaction_id: int = 0,
+    ) -> None:
         """Initialize a new request message."""
         if not write_registers:
             write_registers = []
@@ -135,9 +148,13 @@ def decode(self, data: bytes) -> None:
     async def update_datastore(self, context: ModbusDeviceContext) -> ModbusPDU:
         """Run a write single register request against a datastore."""
         if not (1 <= self.read_count <= 0x07D):
-            return ExceptionResponse(self.function_code, ExceptionResponse.ILLEGAL_VALUE)
+            return ExceptionResponse(
+                self.function_code, ExceptionResponse.ILLEGAL_VALUE
+            )
         if not 1 <= self.write_count <= 0x079:
-            return ExceptionResponse(self.function_code, ExceptionResponse.ILLEGAL_VALUE)
+            return ExceptionResponse(
+                self.function_code, ExceptionResponse.ILLEGAL_VALUE
+            )
         rc = await context.async_setValues(
             self.function_code, self.write_address, self.write_registers
         )
@@ -146,7 +163,11 @@ async def update_datastore(self, context: ModbusDeviceContext) -> ModbusPDU:
         registers = await context.async_getValues(
             self.function_code, self.read_address, self.read_count
         )
-        return ReadWriteMultipleRegistersResponse(registers=cast(list[int], registers), dev_id=self.dev_id, transaction_id=self.transaction_id)
+        return ReadWriteMultipleRegistersResponse(
+            registers=cast(list[int], registers),
+            dev_id=self.dev_id,
+            transaction_id=self.transaction_id,
+        )
 
     def get_response_pdu_size(self) -> int:
         """Get response pdu size.
@@ -174,7 +195,7 @@ def encode(self) -> bytes:
 
     def decode(self, data: bytes) -> None:
         """Decode a write single register packet packet request."""
-        self.address, register = struct.unpack(">HH", data)
+        self.address, register = struct.unpack(">HH", data[:4])
         self.registers = [register]
 
 
@@ -184,14 +205,18 @@ class WriteSingleRegisterRequest(WriteSingleRegisterResponse):
     async def update_datastore(self, context: ModbusDeviceContext) -> ModbusPDU:
         """Run a write single register request against a datastore."""
         if not 0 <= self.registers[0] <= 0xFFFF:
-            return ExceptionResponse(self.function_code, ExceptionResponse.ILLEGAL_VALUE)
+            return ExceptionResponse(
+                self.function_code, ExceptionResponse.ILLEGAL_VALUE
+            )
         rc = await context.async_setValues(
             self.function_code, self.address, self.registers
         )
         if rc:
             return ExceptionResponse(self.function_code, rc)
         values = await context.async_getValues(self.function_code, self.address, 1)
-        return WriteSingleRegisterResponse(address=self.address, registers=cast(list[int], values))
+        return WriteSingleRegisterResponse(
+            address=self.address, registers=cast(list[int], values)
+        )
 
     def get_response_pdu_size(self) -> int:
         """Get response pdu size.
@@ -225,13 +250,20 @@ def decode(self, data: bytes) -> None:
     async def update_datastore(self, context: ModbusDeviceContext) -> ModbusPDU:
         """Run a write single register request against a datastore."""
         if not 1 <= self.count <= 0x07B:
-            return ExceptionResponse(self.function_code, ExceptionResponse.ILLEGAL_VALUE)
+            return ExceptionResponse(
+                self.function_code, ExceptionResponse.ILLEGAL_VALUE
+            )
         rc = await context.async_setValues(
             self.function_code, self.address, self.registers
-          )
+        )
         if rc:
             return ExceptionResponse(self.function_code, rc)
-        return WriteMultipleRegistersResponse(address=self.address, count=self.count, dev_id=self.dev_id, transaction_id=self.transaction_id)
+        return WriteMultipleRegistersResponse(
+            address=self.address,
+            count=self.count,
+            dev_id=self.dev_id,
+            transaction_id=self.transaction_id,
+        )
 
     def get_response_pdu_size(self) -> int:
         """Get response pdu size.
@@ -253,7 +285,7 @@ def encode(self) -> bytes:
 
     def decode(self, data: bytes) -> None:
         """Decode a write single register packet packet request."""
-        self.address, self.count = struct.unpack(">HH", data)
+        self.address, self.count = struct.unpack(">HH", data[:4])
 
 
 class MaskWriteRegisterRequest(ModbusPDU):
@@ -262,7 +294,14 @@ class MaskWriteRegisterRequest(ModbusPDU):
     function_code = 0x16
     rtu_frame_size = 10
 
-    def __init__(self, address=0x0000, and_mask=0xFFFF, or_mask=0x0000, dev_id=1, transaction_id=0) -> None:
+    def __init__(
+        self,
+        address=0x0000,
+        and_mask=0xFFFF,
+        or_mask=0x0000,
+        dev_id=1,
+        transaction_id=0,
+    ) -> None:
         """Initialize a new instance."""
         super().__init__(transaction_id=transaction_id, dev_id=dev_id, address=address)
         self.and_mask = and_mask
@@ -274,22 +313,34 @@ def encode(self) -> bytes:
 
     def decode(self, data: bytes) -> None:
         """Decode the incoming request."""
-        self.address, self.and_mask, self.or_mask = struct.unpack(">HHH", data)
+        self.address, self.and_mask, self.or_mask = struct.unpack(">HHH", data[:6])
 
     async def update_datastore(self, context: ModbusDeviceContext) -> ModbusPDU:
         """Run a mask write register request against the store."""
         if not 0x0000 <= self.and_mask <= 0xFFFF:
-            return ExceptionResponse(self.function_code, ExceptionResponse.ILLEGAL_VALUE)
+            return ExceptionResponse(
+                self.function_code, ExceptionResponse.ILLEGAL_VALUE
+            )
         if not 0x0000 <= self.or_mask <= 0xFFFF:
-            return ExceptionResponse(self.function_code, ExceptionResponse.ILLEGAL_VALUE)
+            return ExceptionResponse(
+                self.function_code, ExceptionResponse.ILLEGAL_VALUE
+            )
         values = await context.async_getValues(self.function_code, self.address, 1)
-        values = (cast(Sequence[int | bool], values)[0] & self.and_mask) | (self.or_mask & ~self.and_mask)
+        values = (cast(Sequence[int | bool], values)[0] & self.and_mask) | (
+            self.or_mask & ~self.and_mask
+        )
         rc = await context.async_setValues(
             self.function_code, self.address, cast(list[int], [values])
         )
         if rc:
             return ExceptionResponse(self.function_code, rc)
-        return MaskWriteRegisterResponse(address=self.address, and_mask=self.and_mask, or_mask=self.or_mask, dev_id=self.dev_id, transaction_id=self.transaction_id)
+        return MaskWriteRegisterResponse(
+            address=self.address,
+            and_mask=self.and_mask,
+            or_mask=self.or_mask,
+            dev_id=self.dev_id,
+            transaction_id=self.transaction_id,
+        )
 
 
 class MaskWriteRegisterResponse(ModbusPDU):
@@ -298,7 +349,14 @@ class MaskWriteRegisterResponse(ModbusPDU):
     function_code = 0x16
     rtu_frame_size = 10
 
-    def __init__(self, address=0x0000, and_mask=0xFFFF, or_mask=0x0000, dev_id=1, transaction_id=0) -> None:
+    def __init__(
+        self,
+        address=0x0000,
+        and_mask=0xFFFF,
+        or_mask=0x0000,
+        dev_id=1,
+        transaction_id=0,
+    ) -> None:
         """Initialize new instance."""
         super().__init__(transaction_id=transaction_id, dev_id=dev_id, address=address)
         self.and_mask = and_mask
@@ -311,4 +369,4 @@ def encode(self) -> bytes:
 
     def decode(self, data: bytes) -> None:
         """Decode a the response."""
-        self.address, self.and_mask, self.or_mask = struct.unpack(">HHH", data)
+        self.address, self.and_mask, self.or_mask = struct.unpack(">HHH", data[:6])


Test patch:
diff --git a/test/framer/test_framer.py b/test/framer/test_framer.py
index b2bf29d78..93f90184b 100644
--- a/test/framer/test_framer.py
+++ b/test/framer/test_framer.py
@@ -69,6 +69,14 @@ def test_roundtrip_CRC(self):
         assert FramerRTU.compute_CRC(data) == 0xE2DB
         assert FramerRTU.check_CRC(data, 0xE2DB)
 
+    async def test_handleFrame2(self):
+        """Test handleFrame."""
+        test_framer = FramerRTU(DecodePDU(True))
+        msg = b"\xfe\x04\x00\x03\x00\x01\xd5\xc5\x00"
+        used_len, pdu = test_framer.handleFrame(msg, 0, 0)
+        assert used_len == len(msg)
+        assert pdu
+
 
 class TestFramerType:
     """Test classes."""