commaai/openpilot · error · RuntimeError
bundled firmware is , expected version
Error message
bundled firmware is {expected_product!r}, expected version {expected_version} What it means
Raised by flash_chestnut() when the caller passes an expected_version argument but the product string embedded in the bundled firmware image does not equal 'custom {expected_version}-CLEAN'. It is a pre-flight sanity check that runs before any hardware is touched, comparing image_product(image) against the version you claimed. The firmware bundle on disk and the version argument you passed disagree, so the flash aborts to avoid writing an unexpected build.
Solutions
- Re-run without the version argument to flash whatever is bundled: sudo python flash.py
- If you know the bundled version, pass exactly that hash: sudo python flash.py <correct-version>
- If the bundle is stale, restore the correct firmware file at FIRMWARE_PATH (git checkout / re-download) and retry with the expected version
- Verify what the bundle actually contains by calling image_product(FIRMWARE_PATH.read_bytes()) and compare to 'custom {version}-CLEAN'
Example fix
# before flash_chestnut(expected_version="abc123") # bundle has a different build # after prod = image_product(FIRMWARE_PATH.read_bytes()) print(prod) # e.g. 'custom def456-CLEAN' flash_chestnut(expected_version="def456")
Defensive patterns
Strategy: validation
Validate before calling
from openpilot.system.hardware.chestnut.flash import FIRMWARE_PATH, image_product, validate_image
image = FIRMWARE_PATH.read_bytes()
product = image_product(image) # e.g. 'custom abc123-CLEAN'
expected = f"custom {args.version}-CLEAN"
assert args.version is None or product == expected, f"bundle={product!r} requested={expected!r}" Try / catch
try:
flash_chestnut(expected_version=ver, force=force)
except RuntimeError as e:
if 'bundled firmware is' in str(e):
print(f"version mismatch: {e}; re-run with the printed product's hash")
raise SystemExit(2)
raise Prevention
- Derive the version argument from the bundle itself: strip 'custom ' and '-CLEAN' from image_product() output instead of hardcoding
- In CI, fail fast by asserting the bundle product string matches the release manifest before invoking flash.py
When it happens
Trigger: Running 'python flash.py <version>' where <version> does not match the product string baked into FIRMWARE_PATH; rebuilding/replacing the bundled firmware image without updating the CLI argument; passing a full product string instead of just the version hash.
Common situations: Version skew after a partial checkout or stale firmware bundle; CI passing the release version while the working tree carries a locally built firmware; typo in the version hash on the command line.
Related errors
- final full-image verification failed
- cannot recover from the ROM bootloader without a config…
- could not disable USB runtime PM
- expected one chestnut, found
- flash did not converge within
AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15).
Data as JSON: /api/errors/8c282e181171e8d2.
Report an issue: GitHub.
Appendix: source
Thrown at openpilot/system/hardware/chestnut/flash.py:460
print(f"chestnut re-enumerated with {product!r}, firmware activates on its next power cycle", flush=True)
return
time.sleep(0.2)
print("chestnut did not re-enumerate, firmware activates on its next power cycle", flush=True)
def defer_signal(signum, _frame):
# writing from a handler must not reenter a print already in progress
os.write(1, f"signal {signum} deferred until the chestnut is powered back up\n".encode())
def flash_chestnut(expected_version=None, force=False):
global _deadline
image = FIRMWARE_PATH.read_bytes()
validate_image(image)
expected_product = image_product(image)
if expected_version is not None and expected_product != f"custom {expected_version}-CLEAN":
raise RuntimeError(f"bundled firmware is {expected_product!r}, expected version {expected_version}")
path, vid_pid, product = find_chestnut()
if path is None:
print("no chestnut connected", flush=True)
return
if product == expected_product and not force:
print(f"chestnut firmware is up to date ({expected_product})", flush=True)
return
_deadline = time.monotonic() + FLASH_BUDGET
for pm_path in PM_PATHS:
disable_runtime_pm(pm_path)
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):
returnView on GitHub (pinned to 516ec1e682)