{"record":{"id":"19b36ab8f9c38ddd","repo":"commaai/openpilot","slug":"could-not-exclusively-lock-port-self-port-e","errorCode":null,"errorMessage":"could not exclusively lock port {self._port}: {e}","messagePattern":"could not exclusively lock port (.+?): (.+?)","errorType":"exception","errorClass":"SerialException","httpStatus":null,"severity":"error","filePath":"openpilot/common/serial.py","lineNumber":81,"sourceCode":"    self._dtr = bool(value)\n    if self._fd >= 0:\n      self._set_line(TIOCM_DTR, _TIOCM_DTR, self._dtr)\n\n  def open(self) -> None:\n    if self._fd >= 0:\n      return\n    try:\n      self._fd = os.open(self._port, os.O_RDWR | os.O_NOCTTY | os.O_NONBLOCK)\n    except OSError as e:\n      self._fd = -1\n      raise SerialException(e.errno, f\"could not open port {self._port}: {e}\") from e\n\n    try:\n      if self._exclusive:\n        try:\n          fcntl.flock(self._fd, fcntl.LOCK_EX | fcntl.LOCK_NB)\n        except OSError as e:\n          raise SerialException(e.errno, f\"could not exclusively lock port {self._port}: {e}\") from e\n\n      self._configure()\n\n      # When not using hardware DSR/DTR handshaking, drive lines ourselves.\n      if not self._dsrdtr:\n        try:\n          self._set_line(TIOCM_DTR, _TIOCM_DTR, self._dtr)\n          if not self._rtscts:\n            self._set_line(TIOCM_RTS, _TIOCM_RTS, True)\n        except OSError as e:\n          if e.errno not in (errno.EINVAL, errno.ENOTTY):\n            raise\n\n      self.reset_input_buffer()\n    except BaseException:\n      self._close_fd()\n      raise\n","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/common/serial.py#L63-L99","documentation":"When opened with exclusive=True, the port takes a non-blocking flock(LOCK_EX). If another process already holds the lock, OSError (EWOULDBLOCK) is converted to SerialException 'could not exclusively lock port'. This is a deliberate single-owner guard for UART devices shared between services.","triggerScenarios":"Opening a port with exclusive=True while another process (e.g. a modem daemon or a previous crashed-but-alive instance) has the device open.","commonSituations":"Two services configured for the same tty; a hung prior process still holding the fd; running a debug tool against a port the main daemon owns.","solutions":["Find and stop the other holder: lsof /dev/ttyUSB0 or fuser","Start your service only after the previous instance fully exited (check supervisor logs)","If sharing is intended, open with exclusive=False — but beware interleaved writes"],"exampleFix":"// before\nser = Serial(port=\"/dev/ttyUSB0\", exclusive=True)\n\n// after\nser = Serial(port=\"/dev/ttyUSB0\", exclusive=False)","handlingStrategy":"validation","validationCode":"import fcntl, os\n\ndef port_lock_free(port: str) -> bool:\n    try:\n        fd = os.open(port, os.O_RDWR | os.O_NONBLOCK)\n    except OSError:\n        return False\n    try:\n        fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)\n        return True\n    except OSError:\n        return False\n    finally:\n        os.close(fd)","typeGuard":null,"tryCatchPattern":"try:\n    ser.open()\nexcept SerialException as e:\n    if \"exclusively lock\" in str(e):\n        report_port_in_use(ser._port)","preventionTips":["lsof the port before starting a second service","Ensure prior instances fully exit before restart","Only pass exclusive=False when concurrent access is truly intended"],"tags":["serial","locking","device-access"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}