commaai/openpilot · error · RuntimeError
bundled firmware is {expected_product!r}, 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.
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)
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
- Unhandled sparse chunk type
- Raw hash mismatch '{raw_hash.hexdigest().lower()}'
- Uncompressed size mismatch
- flash did not converge within {FLASH_BUDGET:g}s
AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15).
Data as JSON: /api/errors/8c282e181171e8d2.
Report an issue: GitHub.