commaai/openpilot · error · RuntimeError

chestnut is in use, stop modeld/GPU processes before flashin

Error message

chestnut is in use, stop modeld/GPU processes before flashing

What it means

Raised by claim_interface() in openpilot/system/hardware/chestnut/flash.py when the USBDEVFS_CLAIMINTERFACE (or SETCONFIGURATION/SETINTERFACE) ioctl returns EBUSY. It means another process currently holds the USB interface of the ASM2464 (chestnut) bridge, almost always the GPU driver stack (modeld) that uses the device in normal operation. The flasher refuses to share the device because EP0 control-register access during flashing would corrupt live GPU traffic.

Source

Thrown at openpilot/system/hardware/chestnut/flash.py:141

  finally:
    os.close(fd)


def claim_interface(path, setup=False):
  # unbind usb-storage, which binds to the ROM bootloader
  disable_runtime_pm(path)
  unbind_drivers(path)
  fd = open_device(path)
  try:
    if setup:
      fcntl.ioctl(fd, USBDEVFS_SETCONFIGURATION, struct.pack("I", 1))
    fcntl.ioctl(fd, USBDEVFS_CLAIMINTERFACE, struct.pack("I", 0))
    if setup:
      fcntl.ioctl(fd, USBDEVFS_SETINTERFACE, struct.pack("II", 0, 0))
  except OSError as e:
    os.close(fd)
    if e.errno == errno.EBUSY:
      raise RuntimeError("chestnut is in use, stop modeld/GPU processes before flashing") from e
    raise
  return fd


class Flash:
  def __init__(self):
    self.fd = -1

  def close(self):
    if self.fd >= 0:
      os.close(self.fd)
      self.fd = -1

  def connect(self, timeout=5.0):
    self.close()
    deadline = time.monotonic() + timeout
    while time.monotonic() < deadline:
      path, vid_pid, product = find_chestnut()

View on GitHub (pinned to 516ec1e682)

Solutions

  1. Stop the processes using the device: systemctl stop the openpilot service or pkill modeld (and anything holding /dev/bus/usb for VID 3801:0001 / add1:0001), then re-run the flasher
  2. Find who holds the device with lsof / grep on /dev/bus/usb or fuser, and kill that process
  3. If a kernel driver is still bound, echo the interface into the driver's unbind node (the script's unbind_drivers does this, but only for interfaces with a driver symlink) or unbind it manually and retry
  4. As a last resort unplug/replug the chestnut enclosure to force a clean enumeration, then retry

Example fix

# before: flashing while modeld runs
sudo python3 flash.py
# RuntimeError: chestnut is in use...

# after: stop users first
pkill modeld; sleep 2
sudo python3 flash.py
Defensive patterns

Strategy: validation

Validate before calling

import glob, os

def chestnut_users():
    users = []
    for d in glob.glob('/sys/bus/usb/devices/*'):
        try:
            vid = open(d + '/idVendor').read().strip()
            pid = open(d + '/idProduct').read().strip()
        except OSError:
            continue
        if (vid, pid) in (('3801', '0001'), ('add1', '0001'), ('174c', '2464'), ('174c', '2463')):
            bus = int(open(d + '/busnum').read()); dev = int(open(d + '/devnum').read())
            users.append(f'/dev/bus/usb/{bus:03d}/{dev:03d}')
    return users  # pass each to lsof/fuser before flashing

Try / catch

try:
    flash.connect()
except RuntimeError as e:
    if 'in use' in str(e):
        stop_modeld_and_retry()  # kill holders, then reconnect
    raise

Prevention

When it happens

Trigger: Calling Flash.connect() or rom_write() (both call claim_interface) while modeld or any process with the chestnut USB interface open is still running. EBUSY comes from the kernel when usbfs cannot claim interface 0 because another driver is bound or another usbfs fd holds a claim.

Common situations: Running the flasher on a live openpilot device without stopping manager/modeld first; a leftover process holding /dev/bus/usb open after a previous aborted flash; usb-storage still bound to the ROM bootloader interface.

Related errors


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