commaai/openpilot · error · SerialException

could not get port attributes: {e}

Error message

could not get port attributes: {e}

What it means

Raised by Serial._configure when termios.tcgetattr on the fd raises termios.error. tcgetattr fails when the fd is not a terminal/serial device, so this almost always means the path opened is not a tty.

Source

Thrown at openpilot/common/serial.py:236

      timeout = 0.0
    try:
      ready, _, _ = select.select([self._fd], [], [], timeout)
    except InterruptedError:
      return False
    return bool(ready)

  def _baud_constant(self, baudrate: int) -> int:
    try:
      return getattr(termios, f"B{baudrate}")
    except AttributeError as e:
      raise ValueError(f"unsupported baud rate: {baudrate}") from e

  def _configure(self) -> None:
    self._ensure_open()
    try:
      attrs = termios.tcgetattr(self._fd)
    except termios.error as e:
      raise SerialException(f"could not get port attributes: {e}") from e

    iflag, oflag, cflag, lflag, _ispeed, _ospeed, cc = attrs

    # raw binary 8N1
    iflag = 0
    oflag = 0
    lflag = 0
    cflag |= termios.CLOCAL | termios.CREAD
    cflag &= ~termios.CSIZE
    cflag |= termios.CS8
    cflag &= ~(termios.PARENB | termios.PARODD | termios.CSTOPB)

    if hasattr(termios, "CRTSCTS"):
      if self._rtscts:
        cflag |= termios.CRTSCTS
      else:
        cflag &= ~termios.CRTSCTS

View on GitHub (pinned to 516ec1e682)

Solutions

  1. Verify the path is a character tty device: ls -l /dev/<name> and confirm it is a tty
  2. Check the fd with os.isatty(fd) before constructing/using Serial
  3. Confirm the device is enumerated (lsusb, dmesg) and the udev symlink points at the real tty

Example fix

// before
ser = Serial('/dev/ttyFAKE0', 115200)

// after
import os
if not os.path.exists('/dev/ttyUSB0') or not open('/dev/ttyUSB0').isatty():
  raise RuntimeError('serial device is not a tty')
ser = Serial('/dev/ttyUSB0', 115200)
Defensive patterns

Strategy: validation

Validate before calling

import os

def is_serial_tty(devpath: str) -> bool:
    try:
        return os.path.isfile(devpath) is False and open(devpath, 'rb').isatty()
    except OSError:
        return False

# use before constructing
assert is_serial_tty(devpath)

Try / catch

from common.serial import SerialException
try:
    ser = Serial(devpath, baudrate)
except SerialException as e:
    if 'could not get port attributes' in str(e):
        raise RuntimeError(f'{devpath} is not a serial/tty device') from e
    raise

Prevention

When it happens

Trigger: Opening a regular file, a FIFO, /dev/urandom, or an already-disappeared /dev/ttyXXX node and then calling the constructor, which invokes _configure.

Common situations: Wrong device path passed to Serial (e.g., /dev/ttyUSB0 no longer exists and the code fell back to another file), a udev race where the node vanishes between open and configure, opening a non-serial gadget endpoint.

Related errors


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