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

  1. Treat each UDP datagram as exactly one sync packet: skip/log datagrams whose length is not 32 instead of parsing
  2. If buffering a byte stream, accumulate until at least 32 bytes and split on the sync magic
  3. 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

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


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/631eb78f99786d26. Report an issue: GitHub.