{"record":{"id":"a7899835374c9d3b","repo":"commaai/openpilot","slug":"write-returned-0","errorCode":null,"errorMessage":"write returned 0","messagePattern":"write returned 0","errorType":"exception","errorClass":"SerialException","httpStatus":null,"severity":"error","filePath":"openpilot/common/serial.py","lineNumber":175,"sourceCode":"\n  def write(self, data: bytes) -> int:\n    self._ensure_open()\n    if not data:\n      return 0\n    view = memoryview(data)\n    total = 0\n    while total < len(data):\n      try:\n        n = os.write(self._fd, view[total:])\n      except InterruptedError:\n        continue\n      except OSError as e:\n        if e.errno in (errno.EAGAIN, errno.EWOULDBLOCK):\n          select.select([], [self._fd], [], None)\n          continue\n        raise SerialException(e.errno, f\"write failed: {e}\") from e\n      if n == 0:\n        raise SerialException(\"write returned 0\")\n      total += n\n    return total\n\n  def flush(self) -> None:\n    self._ensure_open()\n    termios.tcdrain(self._fd)\n\n  def reset_input_buffer(self) -> None:\n    self._ensure_open()\n    termios.tcflush(self._fd, termios.TCIFLUSH)\n\n  def reset_output_buffer(self) -> None:\n    self._ensure_open()\n    termios.tcflush(self._fd, termios.TCOFLUSH)\n\n  def _close_fd(self) -> None:\n    if self._fd >= 0:\n      try:","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/common/serial.py#L157-L193","documentation":"Raised by Serial.write when os.write returns 0 bytes even though the fd accepted the call. POSIX writes of a nonzero buffer returning 0 indicate the driver consumed nothing, which this implementation treats as a hard error rather than looping forever.","triggerScenarios":"os.write(self._fd, view[total:]) returning 0, which can happen with a detached/defunct tty, a USB CDC device in a bad state, or an empty memoryview slipping past the initial 'if not data' guard (e.g., zero-length slice after total catches up).","commonSituations":"Device firmware crashed while still enumerated, writing a zero-length slice from a loop that advances total incorrectly, or a tty in a half-closed state after surprise removal.","solutions":["Verify you are not passing empty/zero-length byte slices to write()","Reopen the serial port: the device is likely in a bad state","Power-cycle the peripheral to reset its USB stack"],"exampleFix":"// before\nser.write(payload[offset:])  # offset can equal len(payload)\n\n// after\nchunk = payload[offset:]\nif chunk:\n  ser.write(chunk)","handlingStrategy":"validation","validationCode":"def safe_write(ser, data: bytes) -> int:\n    if not data:\n        return 0\n    return ser.write(data)","typeGuard":null,"tryCatchPattern":"from common.serial import SerialException\ntry:\n    ser.write(chunk)\nexcept SerialException as e:\n    if str(e) == 'write returned 0':\n        ser.close(); ser = Serial(devpath, baudrate)\n    else:\n        raise","preventionTips":["Never pass zero-length slices; guard slicing loops with 'if chunk:'","Power-cycle the peripheral if writes consistently return 0"],"tags":["serial","hardware","io"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}