commaai/openpilot · critical · RuntimeError
ROM flash command failed
Error message
ROM flash command {cdb[0]:02x} {cdb[1]:02x} failed What it means
RuntimeError from the cmd() helper inside rom_write(): a SCSI BOT (Bulk-Only Transport) command sent to the ROM bootloader returned a Command Status Wrapper whose signature was not 'USBS' or whose status byte (csw[12]) was non-zero - the bootloader rejected or failed the vendor command. The CDB bytes in the message identify the failing recovery step (e.g. 'e3 50' writes firmware block 0, 'e8 51' finalizes).
Solutions
- Re-run the full recovery - ROM commands rewrite from the start, and a retry with a stable link usually succeeds
- Verify the image with validate_image() before recovery so the ROM never gets malformed lengths or data
- Move to a direct, high-quality USB connection; recovery uses 30s bulk timeouts, and hubs and long cables are the classic failure source
- If the same CDB fails deterministically across retries, the flash chip itself is failing in ROM mode - hardware replacement is the remaining option
Defensive patterns
Strategy: retry
Validate before calling
from flash import validate_image, find_chestnut validate_image(image) # reject malformed images before ROM mode assert find_chestnut()[0] is not None # device present before recovery
Try / catch
try:
rom_write(image, config)
except RuntimeError as e:
if 'ROM flash command' in str(e):
stabilize_link() # reseat cable, direct port
rom_write(image, config) # full restart is safe - image rewritten from scratch Prevention
- Validate the image before entering ROM recovery
- Flash recovery on a direct, high-quality USB connection only
- If one CDB fails deterministically across retries, classify it as hardware failure and replace the unit
When it happens
Trigger: rom_write()'s sequence: cmd(0xE1,0x50,...) writes config pages, cmd(0xE3,0x50/0xD0,...) writes firmware blocks, cmd(0xE8,0x51) finalizes. Any of these returning a failed CSW raises - caused by corrupt bulk data on a flaky link, a device-side flash write error, a protocol stall the CLEAR_HALT retry did not clear, or malformed image lengths in the CDB.
Common situations: Interrupted or corrupt bulk transfer on a bad cable during ROM recovery; a modified script sending chunks longer than the 0xFF00 split the ROM command accepts; flash chip hardware fault; commands sent out of order after a partial earlier failure.
Related errors
- chestnut did not re-enumerate after reset
- chestnut disappeared before recovery
- chestnut fell back to the ROM bootloader
- cannot recover from the ROM bootloader without a config…
- chestnut is in use, stop modeld/GPU processes before…
AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15).
Data as JSON: /api/errors/e4656169e7a4b830.
Report an issue: GitHub.
Appendix: source
Thrown at openpilot/system/hardware/chestnut/flash.py:385
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)
except OSError as e:
if e.errno != errno.EPIPE:
raise
fcntl.ioctl(fd, USBDEVFS_CLEAR_HALT, struct.pack("I", 0x81))
csw = bulk(0x81, bytes(13), timeout)
if csw[:4] != b"USBS" or csw[12] != 0:
raise RuntimeError(f"ROM flash command {cdb[0]:02x} {cdb[1]:02x} failed")
print("recovering from the ROM bootloader", flush=True)
try:
cmd(struct.pack(">BBB12x", 0xE1, 0x50, 0), config[:0x80])
cmd(struct.pack(">BBB12x", 0xE1, 0x50, 1), config[0x80:])
cmd(struct.pack(">BBI", 0xE3, 0x50, min(len(image), 0xFF00)), image[:0xFF00])
if len(image) > 0xFF00:
cmd(struct.pack(">BBI", 0xE3, 0xD0, len(image) - 0xFF00), image[0xFF00:])
cmd(struct.pack(">BB13x", 0xE8, 0x51))
finally:
os.close(fd)
print("recovery flash done", flush=True)
def vbus_write(value):
try:
with open(VBUS_PATH, "w") as f:
f.write(value + "\n")View on GitHub (pinned to 516ec1e682)