ruvnet/RuView · error · CSIParseError
Sync packet too short: {len(raw_data)} bytes, need {cls.SIZE
Error message
Sync packet too short: {len(raw_data)} bytes, need {cls.SIZE} What it means
SyncPacketParser.parse requires at least 32 bytes (SIZE=32, HEADER_FMT '<IBBBBQQI4x': magic, node_id, proto_ver, flags, reserved, local_us, epoch_us, sequence) and raises CSIParseError naming both the received and required lengths otherwise. These 32-byte sync packets (magic 0xC511A110) are emitted every CONFIG_C6_SYNC_EVERY_N_FRAMES frames by firmware v0.6.9+ on the same UDP socket as CSI frames.
Source
Thrown at archive/v1/src/hardware/csi_extractor.py:357
magic = struct.unpack_from('<I', data, 0)[0]
if magic == ESP32BinaryParser.MAGIC: # 0xC5110001 — CSI frame
...
elif magic == SyncPacketParser.MAGIC: # 0xC511A110 — sync packet
...
"""
MAGIC = 0xC511A110
SIZE = 32
# <IBBBB QQ IB3x>
# I=magic, B=node_id, B=proto_ver, B=flags, B=reserved,
# Q=local_us, Q=epoch_us, I=sequence, B+3x=reserved
HEADER_FMT = '<IBBBBQQI4x'
@classmethod
def parse(cls, raw_data: bytes) -> SyncPacket:
if len(raw_data) < cls.SIZE:
raise CSIParseError(
f"Sync packet too short: {len(raw_data)} bytes, need {cls.SIZE}"
)
magic, node_id, proto_ver, flags_byte, _, local_us, epoch_us, seq = \
struct.unpack_from(cls.HEADER_FMT, raw_data, 0)
if magic != cls.MAGIC:
raise CSIParseError(f"Sync magic mismatch: got 0x{magic:08x}")
return SyncPacket(
node_id=node_id,
proto_ver=proto_ver,
is_leader=bool(flags_byte & 0x01),
is_valid=bool(flags_byte & 0x02),
smoothed_used=bool(flags_byte & 0x04),
local_us=local_us,
epoch_us=epoch_us,
sequence=seq,
flags_raw=flags_byte,
)
View on GitHub (pinned to 4685618388)
Solutions
- Treat each UDP datagram as exactly one sync packet: skip/log datagrams whose length is not 32 instead of parsing
- If buffering a byte stream, accumulate until at least 32 bytes and split on the sync magic
- Verify firmware v0.6.9+ so sync packets actually use this 32-byte layout
Defensive patterns
Strategy: validation
Validate before calling
SIZE = SyncPacketParser.SIZE # 32
if len(datagram) < SIZE:
logger.debug('short sync datagram (%d bytes), skipping', len(datagram))
continue
sync = SyncPacketParser.parse(datagram) Try / catch
try:
sync = SyncPacketParser.parse(pkt)
except CSIParseError as e:
logger.debug('dropping malformed sync packet: %s', e) Prevention
- Handle each UDP datagram as exactly one 32-byte sync packet; skip datagrams of any other length
- When buffering a byte stream, fill to at least 32 bytes and split on the sync magic before parsing
- Confirm firmware v0.6.9+ (ADR-110) so sync packets use the documented 32-byte layout
When it happens
Trigger: parse() on a buffer holding fewer than 32 bytes; a truncated datagram; reading a TCP-style byte stream with a fixed read size that splits a packet; mixing sync and CSI bytes in one buffer and slicing incorrectly.
Common situations: Collector assuming one-read-per-packet on a stream transport when sync packets are UDP datagrams; buffer drained before being filled; probing the parser with short test bytes.
Related errors
- Sync magic mismatch: got 0x{magic:08x}
- Frame too short: need {self.HEADER_SIZE} bytes, got {len(raw
- Invalid magic: expected 0x{self.MAGIC:08X}, got 0x{magic:08X
- Frame too short for I/Q data: need {expected_len} bytes, got
- fps_hz must be positive, got {fps_hz}
AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16).
Data as JSON: /api/errors/631eb78f99786d26.
Report an issue: GitHub.