commaai/openpilot · error · SerialException
could not exclusively lock port {self._port}: {e}
Error message
could not exclusively lock port {self._port}: {e} What it means
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.
Source
Thrown at openpilot/common/serial.py:81
self._dtr = bool(value)
if self._fd >= 0:
self._set_line(TIOCM_DTR, _TIOCM_DTR, self._dtr)
def open(self) -> None:
if self._fd >= 0:
return
try:
self._fd = os.open(self._port, os.O_RDWR | os.O_NOCTTY | os.O_NONBLOCK)
except OSError as e:
self._fd = -1
raise SerialException(e.errno, f"could not open port {self._port}: {e}") from e
try:
if self._exclusive:
try:
fcntl.flock(self._fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except OSError as e:
raise SerialException(e.errno, f"could not exclusively lock port {self._port}: {e}") from e
self._configure()
# When not using hardware DSR/DTR handshaking, drive lines ourselves.
if not self._dsrdtr:
try:
self._set_line(TIOCM_DTR, _TIOCM_DTR, self._dtr)
if not self._rtscts:
self._set_line(TIOCM_RTS, _TIOCM_RTS, True)
except OSError as e:
if e.errno not in (errno.EINVAL, errno.ENOTTY):
raise
self.reset_input_buffer()
except BaseException:
self._close_fd()
raise
View on GitHub (pinned to 516ec1e682)
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
Example fix
// before ser = Serial(port="/dev/ttyUSB0", exclusive=True) // after ser = Serial(port="/dev/ttyUSB0", exclusive=False)
Defensive patterns
Strategy: validation
Validate before calling
import fcntl, os
def port_lock_free(port: str) -> bool:
try:
fd = os.open(port, os.O_RDWR | os.O_NONBLOCK)
except OSError:
return False
try:
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
return True
except OSError:
return False
finally:
os.close(fd) Try / catch
try:
ser.open()
except SerialException as e:
if "exclusively lock" in str(e):
report_port_in_use(ser._port) Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- could not open port {self._port}: {e}
- AT command timed out
- AT command failed: {line}
- Failed to open ISD-R after retries
- Missing +CGLA response
AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15).
Data as JSON: /api/errors/19b36ab8f9c38ddd.
Report an issue: GitHub.