{"record":{"id":"8f4f843801a395a4","repo":"ruvnet/RuView","slug":"empty-data-received","errorCode":null,"errorMessage":"Empty data received","messagePattern":"Empty data received","errorType":"exception","errorClass":"CSIParseError","httpStatus":null,"severity":"error","filePath":"archive/v1/src/hardware/csi_extractor.py","lineNumber":69,"sourceCode":"\n\nclass ESP32CSIParser:\n    \"\"\"Parser for ESP32 CSI data format.\"\"\"\n    \n    def parse(self, raw_data: bytes) -> CSIData:\n        \"\"\"Parse ESP32 CSI data format.\n        \n        Args:\n            raw_data: Raw bytes from ESP32\n            \n        Returns:\n            Parsed CSI data\n            \n        Raises:\n            CSIParseError: If data format is invalid\n        \"\"\"\n        if not raw_data:\n            raise CSIParseError(\"Empty data received\")\n        \n        try:\n            data_str = raw_data.decode('utf-8')\n            if not data_str.startswith('CSI_DATA:'):\n                raise CSIParseError(\"Invalid ESP32 CSI data format\")\n            \n            # Parse ESP32 format: CSI_DATA:timestamp,antennas,subcarriers,freq,bw,snr,[amp],[phase]\n            parts = data_str[9:].split(',')  # Remove 'CSI_DATA:' prefix\n            \n            timestamp_ms = int(parts[0])\n            num_antennas = int(parts[1])\n            num_subcarriers = int(parts[2])\n            frequency_mhz = float(parts[3])\n            bandwidth_mhz = float(parts[4])\n            snr = float(parts[5])\n            \n            # Convert to proper units\n            frequency = frequency_mhz * 1e6  # MHz to Hz","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/archive/v1/src/hardware/csi_extractor.py#L51-L87","documentation":"First guard in ESP32CSIParser.parse: falsy raw_data (b'', empty bytearray, None) is rejected with CSIParseError('Empty data received') before any decoding. The parser only accepts complete text frames, so zero bytes means the caller's read produced nothing — serial timeout, closed socket/EOF, or a polling loop that raced the producer.","triggerScenarios":"parser.parse(ser.readline()) where pyserial returned b'' because timeout elapsed; parse(reader.read(1024)) at stream EOF; parse(None) from an uninitialized buffer; feeding the parser from a queue that yielded an empty payload.","commonSituations":"pyserial loops with timeout= set (b'' is the timeout signal, not an error); ESP32 node rebooting and dropping the TCP/UDP connection; replaying a capture file and running past the last line.","solutions":["Guard before parsing: only call parse when raw_data is truthy (see validationCode)","Treat b'' as a control signal: on timeout continue the loop, on EOF reconnect or stop the stream","Use newline-delimited reads (readline) with a timeout sized to the CSI frame interval so empty reads are rare"],"exampleFix":"# before\nframe = ser.readline()\ndata = parser.parse(frame)  # CSIParseError when readline() timed out\n\n# after\nframe = ser.readline()\nif not frame:\n    continue  # timeout or EOF: retry / handle disconnect\nframe = frame.strip()\nif frame.startswith(b'CSI_DATA:'):\n    data = parser.parse(frame)","handlingStrategy":"validation","validationCode":"frame = ser.readline()\nif not frame:\n    continue  # read timeout or EOF: nothing to parse\ndata = parser.parse(frame)","typeGuard":null,"tryCatchPattern":"from src.hardware.csi_extractor import CSIParseError\n\ntry:\n    data = parser.parse(frame)\nexcept CSIParseError as e:\n    logger.debug('skipping unparseable frame: %s', e)\n    continue","preventionTips":["Check truthiness of every read result before handing it to a parser — b'' is a timeout/EOF signal, not data","Use readline-style, newline-delimited reads so empty reads are unambiguous control events","Distinguish retryable empty reads (timeout) from terminal ones (EOF/closed socket) in the capture loop"],"tags":["hardware","serial","esp32","parsing","stream"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}