{"record":{"id":"fa1ca01f1e02f02f","repo":"commaai/openpilot","slug":"expected-one-chestnut-found-len-found","errorCode":null,"errorMessage":"expected one chestnut, found {len(found)}","messagePattern":"expected one chestnut, found (.+?)","errorType":"console","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"openpilot/system/hardware/chestnut/flash.py","lineNumber":71,"sourceCode":"  _fields_ = [(\"ep\", ctypes.c_uint), (\"len\", ctypes.c_uint),\n              (\"timeout\", ctypes.c_uint), (\"data\", ctypes.c_void_p)]\n\n\nclass RomFallback(Exception):\n  pass\n\n\ndef find_chestnut():\n  found = []\n  for d in glob.glob(\"/sys/bus/usb/devices/*\"):\n    try:\n      vid_pid = (open(d + \"/idVendor\").read().strip(), open(d + \"/idProduct\").read().strip())\n      if vid_pid in VID_PIDS + ROM_VID_PIDS:\n        found.append((d, vid_pid, open(d + \"/product\").read().strip()))\n    except OSError:\n      pass\n  if len(found) > 1:\n    raise RuntimeError(f\"expected one chestnut, found {len(found)}\")\n  return found[0] if found else (None, None, None)\n\n\ndef in_rom_bootloader(vid_pid, product):\n  # the ROM bootloader reports the config page strings, or its own when the config page is lost\n  return vid_pid in ROM_VID_PIDS or product == ROM_PRODUCT or (product or \"\").startswith(\"AS2462\")\n\n\ndef disable_runtime_pm(path):\n  control = os.path.join(path, \"power/control\")\n  if not os.path.exists(control):\n    return\n  with open(control, \"w\") as f:\n    f.write(\"on\\n\")\n  if open(control).read().strip() != \"on\":\n    raise RuntimeError(f\"could not disable USB runtime PM: {control}\")\n  delay = os.path.join(path, \"power/autosuspend_delay_ms\")\n  if os.path.exists(delay):","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/system/hardware/chestnut/flash.py#L53-L89","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n# two chestnut boards plugged in\nflash_chestnut()  # RuntimeError: expected one chestnut, found 2\n\n// after\n# unplug extra board, keep only target\nflash_chestnut()","handlingStrategy":"validation","validationCode":"from openpilot.system.hardware.chestnut.flash import find_chestnut, VID_PIDS, ROM_VID_PIDS\n\ndef exactly_one_chestnut() -> bool:\n    path, vid_pid, product = find_chestnut()\n    return path is not None and not multiple_candidates()","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["firmware","flashing","usb","hardware","validation"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}