commaai/openpilot · error · ValueError

no product string in wrapped firmware

Error message

no product string in wrapped firmware

What it means

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.

Source

Thrown at openpilot/system/hardware/chestnut/flash.py:268

def validate_image(data):
  if len(data) < 10:
    raise ValueError("wrapped firmware is too short")
  body_len = int.from_bytes(data[:4], "little")
  if body_len > MAX_CODE_SIZE:
    raise ValueError(f"wrapped firmware body exceeds {MAX_CODE_SIZE} bytes")
  if len(data) != body_len + 10 or data[4 + body_len] != 0xA5:
    raise ValueError("invalid wrapped firmware length or magic")
  body = data[4:4 + body_len]
  if data[5 + body_len] != sum(body) & 0xFF:
    raise ValueError("invalid wrapped firmware checksum")
  if data[6 + body_len:] != zlib.crc32(body).to_bytes(4, "little"):
    raise ValueError("invalid wrapped firmware CRC")


def image_product(image):
  match = re.search(rb"custom [0-9a-f]{8}-CLEAN", image)
  if match is None:
    raise ValueError("no product string in wrapped firmware")
  return match.group().decode()


def reconnect(flash):
  attempt = 0
  while True:
    attempt += 1
    check_budget()
    try:
      flash.connect()
      flash.init()
      return
    except (OSError, TimeoutError, RuntimeError) as e:
      print(f"waiting for chestnut (attempt {attempt}): {e}", flush=True)
      time.sleep(1)


def with_retries(flash, label, operation):

View on GitHub (pinned to 516ec1e682)

Solutions

  1. Use the official wrapped firmware artifact from the matching openpilot build - it always embeds the product string
  2. 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)
  3. Do not strip string sections from the firmware binary during packaging
  4. Only flash images built for this product line

Example fix

// firmware source must keep the string reachable
__attribute__((used)) static const char g_product[] = "custom " GIT_SHORT_SHA "-CLEAN";
Defensive patterns

Strategy: validation

Validate before calling

import re

def has_product_string(image: bytes) -> bool:
    return re.search(rb'custom [0-9a-f]{8}-CLEAN', image) is not None

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15). Data as JSON: /api/errors/828af4dd7f09b553. Report an issue: GitHub.