{"record":{"id":"39f0b4ef1c9aac7b","repo":"commaai/openpilot","slug":"read-failed-e","errorCode":null,"errorMessage":"read failed: {e}","messagePattern":"read failed: (.+?)","errorType":"exception","errorClass":"SerialException","httpStatus":null,"severity":"error","filePath":"openpilot/common/serial.py","lineNumber":123,"sourceCode":"    if size <= 0:\n      return b\"\"\n\n    buf = bytearray()\n    deadline = self._deadline()\n    while len(buf) < size:\n      remaining = None if deadline is None else max(0.0, deadline - time.monotonic())\n      if not self._wait_readable(remaining):\n        break\n      try:\n        chunk = os.read(self._fd, size - len(buf))\n      except InterruptedError:\n        continue\n      except OSError as e:\n        if e.errno in (errno.EAGAIN, errno.EWOULDBLOCK):\n          if self._timeout == 0:\n            break\n          continue\n        raise SerialException(e.errno, f\"read failed: {e}\") from e\n      if not chunk:\n        break\n      buf.extend(chunk)\n    return bytes(buf)\n\n  def readline(self) -> bytes:\n    self._ensure_open()\n    buf = bytearray()\n    deadline = self._deadline()\n    while True:\n      remaining = None if deadline is None else max(0.0, deadline - time.monotonic())\n      if deadline is not None and remaining == 0.0 and not buf:\n        # match pyserial: timed-out readline returns empty\n        if not self._wait_readable(0.0):\n          return b\"\"\n      elif not self._wait_readable(remaining):\n        return bytes(buf)\n","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/common/serial.py#L105-L141","documentation":"In read(), any OSError from os.read() other than EAGAIN/EWOULDBLOCK is re-raised as SerialException 'read failed: {e}'. Common errnos: EIO (device disconnected, often on USB unplug), EBADF (fd closed concurrently), EOVERFLOW.","triggerScenarios":"Calling read()/readline() when the USB serial adapter is unplugged mid-read (EIO), the port is closed by another thread (EBADF), or the driver reports a hard error.","commonSituations":"USB cable/adapter disconnect during operation; device firmware crash resetting the CDC-ACM/FTDI device; closing the Serial from another thread while a blocking read is in flight.","solutions":["Catch SerialException around reads and treat it as a disconnect: reopen the port and resync the protocol","Physically reseat / replace the USB adapter or cable if EIO recurs","Ensure only one thread closes the port, coordinated with readers"],"exampleFix":"// before\ndata = ser.read(256)\n\n// after\ntry:\n    data = ser.read(256)\nexcept SerialException:\n    ser.close()\n    time.sleep(1)\n    ser.open()  # reconnect on unplug","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    data = ser.read(256)\nexcept SerialException:\n    ser.close()\n    time.sleep(1.0)\n    ser.open()  # USB replug / device reset recovery","preventionTips":["Wrap all reads in SerialException handling with a reopen-and-resync path","Keep USB serial adapters on reliable cabling","Coordinate port closes with reader threads to avoid EBADF"],"tags":["serial","io","usb","disconnect"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}