{"record":{"id":"f06246f9ef6ea0e7","repo":"commaai/openpilot","slug":"invalid-wrapped-firmware-checksum","errorCode":null,"errorMessage":"invalid wrapped firmware checksum","messagePattern":"invalid wrapped firmware checksum","errorType":"console","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"openpilot/system/hardware/chestnut/flash.py","lineNumber":260,"sourceCode":"\n  def program(self, addr, data):\n    self.write_buffer(data + bytes((-len(data)) % 4))\n    self.write_enable()\n    self.transaction(0x02, addr, len(data), mode=1)\n    self.wait_write_done()\n\n\ndef 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()","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/system/hardware/chestnut/flash.py#L242-L278","documentation":"ValueError from validate_image(): the additive checksum byte at offset body_len+5 does not equal sum(body) & 0xFF. The wrapper stores a simple byte-sum checksum of the firmware body; a mismatch means the body bytes were altered after wrapping - corruption in transfer/storage, or a header length that slices the wrong body window.","triggerScenarios":"validate_image(data) where data[5+body_len] != sum(data[4:4+body_len]) & 0xFF. Produced by any corruption in the body, a wrong body_len header (so the checksummed window is wrong), or a file re-encoded in text mode.","commonSituations":"scp/ftp in ASCII mode flipping bytes; bit rot or partial writes on the target's storage; mixing header and body from different builds when hand-assembling the wrapper; little-endian vs big-endian header mistakes.","solutions":["Re-download or re-copy the firmware image in binary mode and re-validate","Verify transport integrity with sha256 on both ends before flashing","If building the wrapper yourself, compute the checksum byte as sum(body) & 0xFF after the final body bytes are fixed","If the checksum is wrong, treat it as corruption - recopy the file; do not patch the checksum byte"],"exampleFix":"# after (when wrapping)\nchecksum = sum(body) & 0xFF\nwrapped = struct.pack('<I', len(body)) + body + b'\\xa5' + bytes([checksum]) + zlib.crc32(body).to_bytes(4, 'little')","handlingStrategy":"validation","validationCode":"import struct\n\ndef checksum_ok(data: bytes) -> bool:\n    if len(data) < 10:\n        return False\n    body_len = struct.unpack_from('<I', data)[0]\n    if len(data) != body_len + 10:\n        return False\n    return data[5 + body_len] == sum(data[4:4 + body_len]) & 0xFF","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Checksum artifacts on the build host and re-verify on the flash host before flashing","Avoid storage media with known bit-rot for firmware artifacts","Never hand-patch bytes inside a wrapped image"],"tags":["firmware","validation","checksum","corruption"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}