commaai/openpilot · critical · RuntimeError
cannot recover from the ROM bootloader without a config back
Error message
cannot recover from the ROM bootloader without a config backup at {backup} What it means
recover_from_rom() needs a previously saved 0x100-byte config blob (config_path()) to restore the chestnut's configuration while reviving it from the ROM bootloader. If that backup file does not exist, recovery cannot proceed and this RuntimeError is raised. Without the config, the device cannot be brought back onto custom firmware from ROM mode.
Source
Thrown at openpilot/system/hardware/chestnut/flash.py:491
previous = {sig: signal.signal(sig, defer_signal) for sig in (signal.SIGINT, signal.SIGTERM, signal.SIGHUP)}
try:
if in_rom_bootloader(vid_pid, product):
if not recover_from_rom(image, expected_product):
return
# firmware is back, verify it against the bundled image
force, product = True, None
write_image(image, expected_product, product, force)
finally:
for sig, handler in previous.items():
signal.signal(sig, handler)
def recover_from_rom(image, expected_product):
# returns whether the chestnut came back on custom firmware
backup = config_path()
if not os.path.isfile(backup):
raise RuntimeError(f"cannot recover from the ROM bootloader without a config backup at {backup}")
config = open(backup, "rb").read()
if len(config) != 0x100:
raise RuntimeError(f"invalid config backup: {backup}")
committed = False
while True:
check_budget()
path, vid_pid, product = find_chestnut()
if path is None:
if committed:
print("chestnut is offline, recovered firmware boots on its next power cycle", flush=True)
return False
vbus_cycle()
continue
if not in_rom_bootloader(vid_pid, product):
return True
if committed:
print("chestnut stayed powered, recovered firmware boots on its next power cycle", flush=True)View on GitHub (pinned to 516ec1e682)
Solutions
- Check whether the backup exists under another account: look at config_path() for the user that originally ran flash.py and copy it to the current location
- Re-run flash.py from the machine/user where the chestnut was originally flashed so config_path() points at the existing backup
- If no backup exists anywhere, the device must be recovered through comma's official recovery/reflash path - contact support with the device state
- For the future: never delete the file at config_path(); back it up alongside device records
Defensive patterns
Strategy: validation
Validate before calling
import os
from openpilot.system.hardware.chestnut.flash import config_path
backup = config_path()
assert os.path.isfile(backup), f"no config backup at {backup} - do NOT enter recovery without it"
assert os.path.getsize(backup) == 0x100, f"bad backup size {os.path.getsize(backup)}" Try / catch
try:
recover_from_rom(image, expected_product)
except RuntimeError as e:
if 'config backup' in str(e):
# stop recovery attempts; escalate - device must not be power-cycled repeatedly
raise SystemExit(f"recovery blocked: {e}")
raise Prevention
- Treat the config backup at config_path() as critical device state: back it up to durable storage right after every successful flash
- Never run flash.py from accounts whose config_path() differs from the one holding the backup
- Before any risky flash (--force, first flash), confirm the backup exists and is 256 bytes so recovery remains possible
When it happens
Trigger: A flash was interrupted and the device fell into the ROM bootloader, but no successful earlier flash ever wrote a config backup at config_path(); the backup file was deleted or is on a different machine/user's home; first-ever flash on a fresh device failing mid-way before a backup existed.
Common situations: Running flash.py as a different user (root vs user) so config_path() resolves elsewhere; cleaning home directories or /tmp wipes the backup; recovering a device that shipped in a bad state and was never successfully flashed once.
Related errors
- chestnut fell back to the ROM bootloader
- invalid config backup: {backup}
- chestnut disappeared before recovery
- chestnut did not re-enumerate after reset
- ROM flash command {cdb[0]:02x} {cdb[1]:02x} failed
AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15).
Data as JSON: /api/errors/8aee0891ef9ec26a.
Report an issue: GitHub.