commaai/openpilot · error · RuntimeError
expected one chestnut, found {len(found)}
Error message
expected one chestnut, found {len(found)} What it means
RuntimeError from find_chestnut() in the chestnut flasher when more than one USB device matches the known VID:PID sets (normal or ROM bootloader IDs). The flash logic assumes exactly one candidate device; with several, it cannot know which to flash, so it aborts before touching anything.
Source
Thrown at openpilot/system/hardware/chestnut/flash.py:71
_fields_ = [("ep", ctypes.c_uint), ("len", ctypes.c_uint),
("timeout", ctypes.c_uint), ("data", ctypes.c_void_p)]
class RomFallback(Exception):
pass
def find_chestnut():
found = []
for d in glob.glob("/sys/bus/usb/devices/*"):
try:
vid_pid = (open(d + "/idVendor").read().strip(), open(d + "/idProduct").read().strip())
if vid_pid in VID_PIDS + ROM_VID_PIDS:
found.append((d, vid_pid, open(d + "/product").read().strip()))
except OSError:
pass
if len(found) > 1:
raise RuntimeError(f"expected one chestnut, found {len(found)}")
return found[0] if found else (None, None, None)
def in_rom_bootloader(vid_pid, product):
# the ROM bootloader reports the config page strings, or its own when the config page is lost
return vid_pid in ROM_VID_PIDS or product == ROM_PRODUCT or (product or "").startswith("AS2462")
def disable_runtime_pm(path):
control = os.path.join(path, "power/control")
if not os.path.exists(control):
return
with open(control, "w") as f:
f.write("on\n")
if open(control).read().strip() != "on":
raise RuntimeError(f"could not disable USB runtime PM: {control}")
delay = os.path.join(path, "power/autosuspend_delay_ms")
if os.path.exists(delay):View on GitHub (pinned to 516ec1e682)
Solutions
- Unplug all but the single chestnut device you intend to flash and rerun
- Power off/remove the extra unit — including one stuck in ROM bootloader mode from a prior failed flash
- List matches first via lsusb (compare VID:PID) to identify the duplicate
Example fix
// before # two chestnut boards plugged in flash_chestnut() # RuntimeError: expected one chestnut, found 2 // after # unplug extra board, keep only target flash_chestnut()
Defensive patterns
Strategy: validation
Validate before calling
from openpilot.system.hardware.chestnut.flash import find_chestnut, VID_PIDS, ROM_VID_PIDS
def exactly_one_chestnut() -> bool:
path, vid_pid, product = find_chestnut()
return path is not None and not multiple_candidates() Prevention
- Attach exactly one chestnut unit before flashing
- Remove devices stuck in ROM bootloader mode from earlier failed flashes
- Scan with lsusb for the known VID:PID pairs before starting
When it happens
Trigger: Having two chestnut boards (or a second device that shares the AS2462-based VID:PID, including one already sitting in ROM bootloader mode alongside a normally enumerated one) plugged in when the flasher scans /sys/bus/usb/devices.
Common situations: A bench with multiple units under test, a device half-flashed from a previous run enumerating in ROM mode while a second unit is attached, or unrelated hardware reusing the same chip.
Related errors
- flash did not converge within {FLASH_BUDGET:g}s
- could not disable USB runtime PM: {control}
- chestnut is in use, stop modeld/GPU processes before flashin
- chestnut fell back to the ROM bootloader
- flash controller timeout
AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15).
Data as JSON: /api/errors/fa1ca01f1e02f02f.
Report an issue: GitHub.