commaai/openpilot · error · RuntimeError
chestnut did not re-enumerate after reset
Error message
chestnut did not re-enumerate after reset
What it means
RuntimeError in rom_write(): after issuing USBDEVFS_RESET on the ROM bootloader device and sleeping 3s, find_chestnut() still finds no device. The ROM bootloader requires a port reset before bulk transfers; the expected flow is reset, re-enumeration, then claim with SETCONFIGURATION. If nothing re-appears within 3 seconds, the post-reset re-enumeration failed.
Source
Thrown at openpilot/system/hardware/chestnut/flash.py:360
os.fsync(f.fileno())
return data
def rom_write(image, config):
# the ROM bootloader implements only the BOT protocol, and requires a port reset before bulk transfers
path, _, _ = find_chestnut()
if path is None:
raise RuntimeError("chestnut disappeared before recovery")
unbind_drivers(path)
fd = open_device(path)
try:
fcntl.ioctl(fd, USBDEVFS_RESET)
finally:
os.close(fd)
time.sleep(3)
path, _, _ = find_chestnut()
if path is None:
raise RuntimeError("chestnut did not re-enumerate after reset")
fd = claim_interface(path, setup=True)
for ep in (0x02, 0x81):
fcntl.ioctl(fd, USBDEVFS_CLEAR_HALT, struct.pack("I", ep))
tag = 0
def bulk(ep, payload, timeout):
buf = ctypes.create_string_buffer(bytes(payload), len(payload))
fcntl.ioctl(fd, USBDEVFS_BULK, Bulk(ep, len(payload), timeout, ctypes.cast(buf, ctypes.c_void_p)))
return buf.raw
def cmd(cdb, data=b"", timeout=30000):
nonlocal tag
tag += 1
bulk(0x02, struct.pack("<IIIBBB16s", 0x43425355, tag, len(data), 0, 0, len(cdb), cdb), timeout)
if data:
bulk(0x02, data, timeout)
try:
csw = bulk(0x81, bytes(13), timeout)View on GitHub (pinned to 516ec1e682)
Solutions
- Wait a few seconds and check lsusb manually - if the device shows up late, re-run; consider increasing the sleep locally for slow hosts
- Check that nothing (usb-storage) grabbed the re-enumerated ROM interface before the next claim - unbind if needed
- Try a different USB port, direct connection preferred; some hubs handle USBDEVFS_RESET poorly
- If the device never comes back after reset, power-cycle the enclosure physically and start recovery over; repeated failure suggests hardware fault or insufficient power
Defensive patterns
Strategy: retry
Validate before calling
import time
from flash import find_chestnut
def waits_for_renumeration(seconds=10) -> bool:
end = time.monotonic() + seconds
while time.monotonic() < end:
if find_chestnut()[0] is not None:
return True
time.sleep(0.5)
return False Try / catch
try:
rom_write(image, config)
except RuntimeError as e:
if 're-enumerate' in str(e):
if waits_for_renumeration(15):
rom_write(image, config)
else:
power_cycle_enclosure() Prevention
- Use ports/controllers known to handle USBDEVFS_RESET cleanly (direct, not hub)
- Give slow devices time after reset before assuming failure
- Power-cycle the enclosure as the standard reset-retry escalation
When it happens
Trigger: rom_write() executes ioctl(fd, USBDEVFS_RESET), closes the fd, sleeps 3s, rescans: None means raise. Happens when the reset power-cycles the device into a state that does not re-enumerate (dead flash config, power problem), when 3s is too short for a slow device/host combination, or when the reset made the host re-bind a driver that hides the device.
Common situations: Slow hubs or controllers handling USBDEVFS_RESET poorly; device drawing power renegotiation after reset; insufficient bus power; a device that only comes back after a full power cycle.
Related errors
- chestnut disappeared before recovery
- chestnut fell back to the ROM bootloader
- ROM flash command {cdb[0]:02x} {cdb[1]:02x} failed
- chestnut did not enumerate within {timeout:g}s
- cannot recover from the ROM bootloader without a config back
AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15).
Data as JSON: /api/errors/f71fe0564cb9a695.
Report an issue: GitHub.