{"record":{"id":"8c282e181171e8d2","repo":"commaai/openpilot","slug":"bundled-firmware-is-expected-product-r-expected","errorCode":null,"errorMessage":"bundled firmware is {expected_product!r}, expected version {expected_version}","messagePattern":"bundled firmware is (.+?), expected version (.+?)","errorType":"console","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"openpilot/system/hardware/chestnut/flash.py","lineNumber":460,"sourceCode":"        print(f\"chestnut re-enumerated with {product!r}, firmware activates on its next power cycle\", flush=True)\n      return\n    time.sleep(0.2)\n  print(\"chestnut did not re-enumerate, firmware activates on its next power cycle\", flush=True)\n\n\ndef defer_signal(signum, _frame):\n  # writing from a handler must not reenter a print already in progress\n  os.write(1, f\"signal {signum} deferred until the chestnut is powered back up\\n\".encode())\n\n\ndef flash_chestnut(expected_version=None, force=False):\n  global _deadline\n\n  image = FIRMWARE_PATH.read_bytes()\n  validate_image(image)\n  expected_product = image_product(image)\n  if expected_version is not None and expected_product != f\"custom {expected_version}-CLEAN\":\n    raise RuntimeError(f\"bundled firmware is {expected_product!r}, expected version {expected_version}\")\n\n  path, vid_pid, product = find_chestnut()\n  if path is None:\n    print(\"no chestnut connected\", flush=True)\n    return\n  if product == expected_product and not force:\n    print(f\"chestnut firmware is up to date ({expected_product})\", flush=True)\n    return\n\n  _deadline = time.monotonic() + FLASH_BUDGET\n  for pm_path in PM_PATHS:\n    disable_runtime_pm(pm_path)\n\n  previous = {sig: signal.signal(sig, defer_signal) for sig in (signal.SIGINT, signal.SIGTERM, signal.SIGHUP)}\n  try:\n    if in_rom_bootloader(vid_pid, product):\n      if not recover_from_rom(image, expected_product):\n        return","sourceCodeStart":442,"sourceCodeEnd":478,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/system/hardware/chestnut/flash.py#L442-L478","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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'"],"exampleFix":"# before\nflash_chestnut(expected_version=\"abc123\")  # bundle has a different build\n\n# after\nprod = image_product(FIRMWARE_PATH.read_bytes())\nprint(prod)  # e.g. 'custom def456-CLEAN'\nflash_chestnut(expected_version=\"def456\")","handlingStrategy":"validation","validationCode":"from openpilot.system.hardware.chestnut.flash import FIRMWARE_PATH, image_product, validate_image\nimage = FIRMWARE_PATH.read_bytes()\nproduct = image_product(image)  # e.g. 'custom abc123-CLEAN'\nexpected = f\"custom {args.version}-CLEAN\"\nassert args.version is None or product == expected, f\"bundle={product!r} requested={expected!r}\"","typeGuard":null,"tryCatchPattern":"try:\n    flash_chestnut(expected_version=ver, force=force)\nexcept RuntimeError as e:\n    if 'bundled firmware is' in str(e):\n        print(f\"version mismatch: {e}; re-run with the printed product's hash\")\n        raise SystemExit(2)\n    raise","preventionTips":["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"],"tags":["firmware","flashing","version-mismatch","openpilot"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}