{"record":{"id":"e0c486d8e23e048f","repo":"commaai/openpilot","slug":"could-not-get-port-attributes-e","errorCode":null,"errorMessage":"could not get port attributes: {e}","messagePattern":"could not get port attributes: (.+?)","errorType":"exception","errorClass":"SerialException","httpStatus":null,"severity":"error","filePath":"openpilot/common/serial.py","lineNumber":236,"sourceCode":"      timeout = 0.0\n    try:\n      ready, _, _ = select.select([self._fd], [], [], timeout)\n    except InterruptedError:\n      return False\n    return bool(ready)\n\n  def _baud_constant(self, baudrate: int) -> int:\n    try:\n      return getattr(termios, f\"B{baudrate}\")\n    except AttributeError as e:\n      raise ValueError(f\"unsupported baud rate: {baudrate}\") from e\n\n  def _configure(self) -> None:\n    self._ensure_open()\n    try:\n      attrs = termios.tcgetattr(self._fd)\n    except termios.error as e:\n      raise SerialException(f\"could not get port attributes: {e}\") from e\n\n    iflag, oflag, cflag, lflag, _ispeed, _ospeed, cc = attrs\n\n    # raw binary 8N1\n    iflag = 0\n    oflag = 0\n    lflag = 0\n    cflag |= termios.CLOCAL | termios.CREAD\n    cflag &= ~termios.CSIZE\n    cflag |= termios.CS8\n    cflag &= ~(termios.PARENB | termios.PARODD | termios.CSTOPB)\n\n    if hasattr(termios, \"CRTSCTS\"):\n      if self._rtscts:\n        cflag |= termios.CRTSCTS\n      else:\n        cflag &= ~termios.CRTSCTS\n","sourceCodeStart":218,"sourceCodeEnd":254,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/common/serial.py#L218-L254","documentation":"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.","triggerScenarios":"Opening a regular file, a FIFO, /dev/urandom, or an already-disappeared /dev/ttyXXX node and then calling the constructor, which invokes _configure.","commonSituations":"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.","solutions":["Verify the path is a character tty device: ls -l /dev/<name> and confirm it is a tty","Check the fd with os.isatty(fd) before constructing/using Serial","Confirm the device is enumerated (lsusb, dmesg) and the udev symlink points at the real tty"],"exampleFix":"// before\nser = Serial('/dev/ttyFAKE0', 115200)\n\n// after\nimport os\nif not os.path.exists('/dev/ttyUSB0') or not open('/dev/ttyUSB0').isatty():\n  raise RuntimeError('serial device is not a tty')\nser = Serial('/dev/ttyUSB0', 115200)","handlingStrategy":"validation","validationCode":"import os\n\ndef is_serial_tty(devpath: str) -> bool:\n    try:\n        return os.path.isfile(devpath) is False and open(devpath, 'rb').isatty()\n    except OSError:\n        return False\n\n# use before constructing\nassert is_serial_tty(devpath)","typeGuard":null,"tryCatchPattern":"from common.serial import SerialException\ntry:\n    ser = Serial(devpath, baudrate)\nexcept SerialException as e:\n    if 'could not get port attributes' in str(e):\n        raise RuntimeError(f'{devpath} is not a serial/tty device') from e\n    raise","preventionTips":["Resolve device paths via udev symlinks rather than hardcoded tty names","Check the node exists and is a tty before open()"],"tags":["serial","termios","configuration"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}