commaai/openpilot · error · SerialException

write returned 0

Error message

write returned 0

What it means

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.

Source

Thrown at openpilot/common/serial.py:175

  def write(self, data: bytes) -> int:
    self._ensure_open()
    if not data:
      return 0
    view = memoryview(data)
    total = 0
    while total < len(data):
      try:
        n = os.write(self._fd, view[total:])
      except InterruptedError:
        continue
      except OSError as e:
        if e.errno in (errno.EAGAIN, errno.EWOULDBLOCK):
          select.select([], [self._fd], [], None)
          continue
        raise SerialException(e.errno, f"write failed: {e}") from e
      if n == 0:
        raise SerialException("write returned 0")
      total += n
    return total

  def flush(self) -> None:
    self._ensure_open()
    termios.tcdrain(self._fd)

  def reset_input_buffer(self) -> None:
    self._ensure_open()
    termios.tcflush(self._fd, termios.TCIFLUSH)

  def reset_output_buffer(self) -> None:
    self._ensure_open()
    termios.tcflush(self._fd, termios.TCOFLUSH)

  def _close_fd(self) -> None:
    if self._fd >= 0:
      try:

View on GitHub (pinned to 516ec1e682)

Solutions

  1. Verify you are not passing empty/zero-length byte slices to write()
  2. Reopen the serial port: the device is likely in a bad state
  3. Power-cycle the peripheral to reset its USB stack

Example fix

// before
ser.write(payload[offset:])  # offset can equal len(payload)

// after
chunk = payload[offset:]
if chunk:
  ser.write(chunk)
Defensive patterns

Strategy: validation

Validate before calling

def safe_write(ser, data: bytes) -> int:
    if not data:
        return 0
    return ser.write(data)

Try / catch

from common.serial import SerialException
try:
    ser.write(chunk)
except SerialException as e:
    if str(e) == 'write returned 0':
        ser.close(); ser = Serial(devpath, baudrate)
    else:
        raise

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15). Data as JSON: /api/errors/a7899835374c9d3b. Report an issue: GitHub.