{"record":{"id":"828af4dd7f09b553","repo":"commaai/openpilot","slug":"no-product-string-in-wrapped-firmware","errorCode":null,"errorMessage":"no product string in wrapped firmware","messagePattern":"no product string in wrapped firmware","errorType":"console","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"openpilot/system/hardware/chestnut/flash.py","lineNumber":268,"sourceCode":"def validate_image(data):\n  if len(data) < 10:\n    raise ValueError(\"wrapped firmware is too short\")\n  body_len = int.from_bytes(data[:4], \"little\")\n  if body_len > MAX_CODE_SIZE:\n    raise ValueError(f\"wrapped firmware body exceeds {MAX_CODE_SIZE} bytes\")\n  if len(data) != body_len + 10 or data[4 + body_len] != 0xA5:\n    raise ValueError(\"invalid wrapped firmware length or magic\")\n  body = data[4:4 + body_len]\n  if data[5 + body_len] != sum(body) & 0xFF:\n    raise ValueError(\"invalid wrapped firmware checksum\")\n  if data[6 + body_len:] != zlib.crc32(body).to_bytes(4, \"little\"):\n    raise ValueError(\"invalid wrapped firmware CRC\")\n\n\ndef image_product(image):\n  match = re.search(rb\"custom [0-9a-f]{8}-CLEAN\", image)\n  if match is None:\n    raise ValueError(\"no product string in wrapped firmware\")\n  return match.group().decode()\n\n\ndef reconnect(flash):\n  attempt = 0\n  while True:\n    attempt += 1\n    check_budget()\n    try:\n      flash.connect()\n      flash.init()\n      return\n    except (OSError, TimeoutError, RuntimeError) as e:\n      print(f\"waiting for chestnut (attempt {attempt}): {e}\", flush=True)\n      time.sleep(1)\n\n\ndef with_retries(flash, label, operation):","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/system/hardware/chestnut/flash.py#L250-L286","documentation":"ValueError from image_product(): it greps the image for the ASCII marker 'custom XXXXXXXX-CLEAN' (8 hex chars, e.g. a git short SHA) to identify which build the firmware targets. If the regex finds no match, the image has no embedded product string and the tool cannot tell whether the image matches this device's expected product.","triggerScenarios":"image_product(image) on a wrapped image whose body was built without the product/version string baked in - e.g. a raw vendor firmware, a locally compiled binary missing the version-string section, or a stripped build that dropped the literal string.","commonSituations":"Flashing a third-party or upstream ASM2464 firmware that never embeds openpilot's 'custom <sha>-CLEAN' string; building firmware with the version define omitted; a linker garbage-collection pass that dropped the string from the final binary.","solutions":["Use the official wrapped firmware artifact from the matching openpilot build - it always embeds the product string","If self-building, ensure the firmware source embeds the literal 'custom <8-hex-git-sha>-CLEAN' string and keeps it alive (used attribute or KEEP() in the linker script)","Do not strip string sections from the firmware binary during packaging","Only flash images built for this product line"],"exampleFix":"// firmware source must keep the string reachable\n__attribute__((used)) static const char g_product[] = \"custom \" GIT_SHORT_SHA \"-CLEAN\";","handlingStrategy":"validation","validationCode":"import re\n\ndef has_product_string(image: bytes) -> bool:\n    return re.search(rb'custom [0-9a-f]{8}-CLEAN', image) is not None","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build firmware with the product string compiled in and marked used/KEEP","Only flash artifacts from the matching product build pipeline","The packaging step should assert image_product() succeeds before publish"],"tags":["firmware","validation","product-id","build"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}