ruvnet/RuView · error · CSIParseError
Invalid ESP32 CSI data format
Error message
Invalid ESP32 CSI data format
What it means
After utf-8 decoding, ESP32CSIParser requires the payload to start with the literal 'CSI_DATA:' prefix; anything else raises CSIParseError('Invalid ESP32 CSI data format'). This parser understands only that exact CSV text protocol, so the error means the bytes on the wire are not a text CSI frame — logs, boot output, binary frames, or garbage.
Source
Thrown at archive/v1/src/hardware/csi_extractor.py:74
def parse(self, raw_data: bytes) -> CSIData:
"""Parse ESP32 CSI data format.
Args:
raw_data: Raw bytes from ESP32
Returns:
Parsed CSI data
Raises:
CSIParseError: If data format is invalid
"""
if not raw_data:
raise CSIParseError("Empty data received")
try:
data_str = raw_data.decode('utf-8')
if not data_str.startswith('CSI_DATA:'):
raise CSIParseError("Invalid ESP32 CSI data format")
# Parse ESP32 format: CSI_DATA:timestamp,antennas,subcarriers,freq,bw,snr,[amp],[phase]
parts = data_str[9:].split(',') # Remove 'CSI_DATA:' prefix
timestamp_ms = int(parts[0])
num_antennas = int(parts[1])
num_subcarriers = int(parts[2])
frequency_mhz = float(parts[3])
bandwidth_mhz = float(parts[4])
snr = float(parts[5])
# Convert to proper units
frequency = frequency_mhz * 1e6 # MHz to Hz
bandwidth = bandwidth_mhz * 1e6 # MHz to Hz
# Parse amplitude and phase arrays from the remaining CSV fields.
# Expected format after the header fields: comma-separated float values
# representing interleaved amplitude and phase per antenna per subcarrier.View on GitHub (pinned to 4685618388)
Solutions
- Filter before parsing: only pass bytes whose stripped value starts with b'CSI_DATA:' (see validationCode)
- If the firmware emits ADR-018 binary frames, construct CSIExtractor with parser_format='binary' so ESP32BinaryParser is selected
- Flash a firmware build that outputs the CSI_DATA CSV line format per docs/hardware-setup.md
Example fix
# before
for chunk in stream:
data = parser.parse(chunk) # raises on every log/boot line
# after
for line in stream:
line = line.strip()
if not line.startswith(b'CSI_DATA:'):
continue # skip console noise
data = parser.parse(line) Defensive patterns
Strategy: validation
Validate before calling
line = raw.strip()
if not line.startswith(b'CSI_DATA:'):
return None # console/log noise or wrong protocol
return parser.parse(line) Type guard
def is_esp32_text_csi(raw: bytes) -> bool:
return bool(raw) and raw.strip().startswith(b'CSI_DATA:') Try / catch
try:
data = parser.parse(raw)
except CSIParseError as e:
logger.debug('non-CSI line dropped: %r -> %s', raw[:40], e) Prevention
- Prefix-filter every line before parsing so firmware logs never reach the parser
- Set parser_format='binary' in the extractor config when the node streams ADR-018 frames
- Pin the firmware build so the CSI_DATA line layout matches what the parser expects
When it happens
Trigger: Feeding ESP32 boot/log lines ('rst:0x1 ...', 'I (320) wifi:...') printed on the same UART; sending an ADR-018 binary frame while config parser_format is not 'binary'; leading whitespace/CR before the prefix; firmware that prints a different CSV header.
Common situations: Firmware console logs interleaved with CSI output on one serial line; forgetting parser_format='binary' when the node streams ADR-018 frames; chunked reads that begin mid-line so the prefix is missed.
Related errors
- ESP32 CSI data incomplete: expected {expected_values} values
- ESP32 CSI data contains non-numeric values: {ve}. Raw CSI fi
- Failed to parse ESP32 data: {e}
- Empty data received
- Frame too short: need {self.HEADER_SIZE} bytes, got {len(raw
AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16).
Data as JSON: /api/errors/8d045f6635e24134.
Report an issue: GitHub.