ruvnet/RuView · error · CSIParseError
Frame too short: need {self.HEADER_SIZE} bytes, got {len(raw
Error message
Frame too short: need {self.HEADER_SIZE} bytes, got {len(raw_data)} What it means
ESP32BinaryParser (ADR-018 frames, selected via config parser_format='binary') rejects any buffer shorter than the 20-byte header (HEADER_SIZE=20, format '<IBBHIIBBBB': magic, node_id, antennas, subcarriers, freq, seq, rssi, noise, ppdu, flags) before attempting struct.unpack. This is a framing problem: not enough bytes exist to even read the header.
Source
Thrown at archive/v1/src/hardware/csi_extractor.py:189
PPDU_HE_MU = 2
PPDU_HE_TB = 3
PPDU_UNKNOWN = 0xFF
_PPDU_NAMES = {0: 'ht_legacy', 1: 'he_su', 2: 'he_mu', 3: 'he_tb', 0xFF: 'unknown'}
def parse(self, raw_data: bytes) -> CSIData:
"""Parse an ADR-018 binary frame into CSIData.
Args:
raw_data: Raw binary frame bytes.
Returns:
Parsed CSI data with amplitude/phase arrays shaped (n_antennas, n_subcarriers).
Raises:
CSIParseError: If frame is too short, has invalid magic, or malformed I/Q data.
"""
if len(raw_data) < self.HEADER_SIZE:
raise CSIParseError(
f"Frame too short: need {self.HEADER_SIZE} bytes, got {len(raw_data)}"
)
magic, node_id, n_antennas, n_subcarriers, freq_mhz, sequence, rssi_u8, noise_u8, \
ppdu_byte, flags_byte = struct.unpack_from(self.HEADER_FMT, raw_data, 0)
if magic != self.MAGIC:
raise CSIParseError(
f"Invalid magic: expected 0x{self.MAGIC:08X}, got 0x{magic:08X}"
)
# Convert unsigned bytes to signed i8
rssi = rssi_u8 if rssi_u8 < 128 else rssi_u8 - 256
noise_floor = noise_u8 if noise_u8 < 128 else noise_u8 - 256
iq_count = n_antennas * n_subcarriers
iq_bytes = iq_count * 2
expected_len = self.HEADER_SIZE + iq_bytesView on GitHub (pinned to 4685618388)
Solutions
- Buffer incoming bytes and call parse only when at least HEADER_SIZE (20) bytes are available; for UDP, parse each datagram as one frame
- Resync the stream: scan for the 4-byte little-endian magic 0xC5110001 before treating bytes as a frame start
- Confirm parser_format matches what the firmware sends (text CSV vs binary ADR-018)
Example fix
# before
frame = await reader.read(64) # may return a partial frame
data = parser.parse(frame) # CSIParseError: Frame too short
# after
buf = bytearray()
while len(buf) < parser.HEADER_SIZE:
buf += await reader.read(parser.HEADER_SIZE - len(buf))
# then read the I/Q payload declared by the header before parsing Defensive patterns
Strategy: validation
Validate before calling
HEADER_SIZE = ESP32BinaryParser.HEADER_SIZE # 20
if len(buf) < HEADER_SIZE:
# not a complete frame yet: keep buffering / skip fragment
continue
data = parser.parse(buf) Type guard
def is_frame_sized(buf: bytes) -> bool:
return len(buf) >= ESP32BinaryParser.HEADER_SIZE Try / catch
try:
data = parser.parse(frame_bytes)
except CSIParseError as e:
logger.debug('dropping short binary frame: %s', e)
resync_buffer() Prevention
- For UDP, parse each datagram as one frame; for byte streams, buffer until at least 20 bytes (then the full declared length)
- Never assume TCP read sizes align with frame boundaries — accumulate and frame on magic
- Log dropped fragments with their lengths: a run of small fragments means the framing loop, not the transport, is wrong
When it happens
Trigger: parse() on a fragment (e.g. first 8 bytes of a frame from a chunked TCP read); an empty buffer after stream EOF; handing ASCII 'CSI_DATA' text to the binary parser; a UDP datagram split by an intermediary.
Common situations: Starting to read mid-frame so the first buffer is the tail of a previous frame; assuming TCP byte-stream reads align with frame boundaries; collector slicing the stream at a fixed size unrelated to frame length.
Related errors
- Invalid magic: expected 0x{self.MAGIC:08X}, got 0x{magic:08X
- Frame too short for I/Q data: need {expected_len} bytes, got
- Sync packet too short: {len(raw_data)} bytes, need {cls.SIZE
- Empty data received
- Invalid ESP32 CSI data format
AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16).
Data as JSON: /api/errors/b3d1649b7fd535a1.
Report an issue: GitHub.