commaai/openpilot · error · RomFallback

chestnut fell back to the ROM bootloader

Error message

chestnut fell back to the ROM bootloader

What it means

RomFallback is raised by Flash.connect() when find_chestnut() reports a VID:PID or product string matching the ASM2464 ROM bootloader (174c:2464/174c:2463, 'USB 3.2 PCIe TinyEnclosure', or 'AS2462*'). It means the device lost (or never had) valid firmware in SPI flash, so it fell back to the mask-ROM bootloader. This is not a hard failure: the tool's main flow catches RomFallback and routes to rom_write() to reflash via the ROM's USB BOT protocol.

Source

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

  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()
      if in_rom_bootloader(vid_pid, product):
        raise RomFallback("chestnut fell back to the ROM bootloader")
      if path is not None:
        self.fd = claim_interface(path)
        return
      time.sleep(0.1)
    raise RuntimeError(f"chestnut did not enumerate within {timeout:g}s")

  def reg_write(self, addr, value):
    fcntl.ioctl(self.fd, USBDEVFS_CONTROL,
                Ctrl(0x40, 0xE5, addr & 0xFFFF, value & 0xFFFF, 0, 2000, None))

  def reg_read(self, addr, length=1):
    buf = (ctypes.c_ubyte * length)()
    fcntl.ioctl(self.fd, USBDEVFS_CONTROL,
                Ctrl(0xC0, 0xE4, addr & 0xFFFF, 0, length, 2000, ctypes.cast(buf, ctypes.c_void_p)))
    return bytes(buf)

  def write_buffer(self, data):
    for i, value in enumerate(data):

View on GitHub (pinned to 516ec1e682)

Solutions

  1. Let the tool handle it: ensure the caller catches RomFallback and invokes rom_write(image, config) - the recovery path is built in
  2. Verify the wrapped image is valid (validate_image) before recovery so rom_write does not reflash garbage
  3. Check power/USB-C connection stability so the ROM-mode bulk transfers (30s timeouts) are not interrupted mid-recovery
  4. If it keeps falling back after successful recovery, suspect failing SPI flash chip or bad VBUS/wiring

Example fix

# before
flash.connect()  # RomFallback escapes and crashes the run

# after
try:
    flash.connect()
except RomFallback:
    rom_write(image, config)  # ROM bootloader recovery, then reconnect
Defensive patterns

Strategy: fallback

Validate before calling

from flash import find_chestnut, in_rom_bootloader

path, vid_pid, product = find_chestnut()
if in_rom_bootloader(vid_pid, product):
    plan_rom_recovery()  # prepare rom_write(image, config) path

Type guard

from flash import RomFallback

def is_rom_fallback(e: BaseException) -> bool:
    return isinstance(e, RomFallback)

Try / catch

try:
    flash.connect()
except RomFallback:
    rom_write(image, config)
    flash.connect()  # device now runs real firmware

Prevention

When it happens

Trigger: Flash.connect() during initial enumeration, or reconnect() after an erase/program brick: the SPI contents are empty/corrupt, so the ASM2464 boots its ROM bootloader and enumerates as 174c:2464 with a usb-storage-compatible BOT interface. Also occurs on a brand-new enclosure with unprogrammed flash.

Common situations: First-time provisioning of a chestnut enclosure; recovery after a flash was interrupted mid-erase (sector erased, firmware not yet written); watchdog or power loss during programming; a device whose firmware region reads all 0xFF.

Related errors


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