{"record":{"id":"85ee94d68ce3cd84","repo":"commaai/openpilot","slug":"uncompressed-hash-mismatch","errorCode":null,"errorMessage":"Uncompressed hash mismatch","messagePattern":"Uncompressed hash mismatch","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"critical","filePath":"openpilot/common/hardware/comma/agnos.py","lineNumber":179,"sourceCode":"\n  with open(path, 'wb+') as out:\n    # Flash partition\n    last_p = 0\n    raw_hash = hashlib.sha256()\n    f = unsparsify if partition['sparse'] else noop\n    for chunk in f(downloader):\n      raw_hash.update(chunk)\n      out.write(chunk)\n      p = int(out.tell() / partition['size'] * 100)\n      if p != last_p:\n        last_p = p\n        print(f\"Installing {partition['name']}: {p}\", flush=True)\n\n    if raw_hash.hexdigest().lower() != partition['hash_raw'].lower():\n      raise Exception(f\"Raw hash mismatch '{raw_hash.hexdigest().lower()}'\")\n\n    if downloader.sha256.hexdigest().lower() != partition['hash'].lower():\n      raise Exception(\"Uncompressed hash mismatch\")\n\n    if out.tell() != partition['size']:\n      raise Exception(\"Uncompressed size mismatch\")\n\n    os.sync()\n\n\ndef flash_partition(target_slot_number: int, partition: dict, cloudlog, standalone=False):\n  cloudlog.info(f\"Downloading and writing {partition['name']}\")\n\n  if verify_partition(target_slot_number, partition):\n    cloudlog.info(f\"Already flashed {partition['name']}\")\n    return\n\n  # Clear hash before flashing in case we get interrupted\n  full_check = partition['full_check']\n  if not full_check:\n    clear_partition_hash(target_slot_number, partition)","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/common/hardware/comma/agnos.py#L161-L197","documentation":"The compressed bytes received from the downloader are hashed (downloader.sha256) and compared against partition['hash']. A mismatch means the compressed stream itself is corrupted even before decompression — network corruption, proxy mangling, or a stale/cached partial file.","triggerScenarios":"flash_partition where the downloaded (still-compressed) stream hash differs from the manifest hash — truncated HTTP response, corrupt gzip/bzip stream, or wrong URL.","commonSituations":"Flaky Wi-Fi during multi-hundred-MB firmware download; interrupted resume serving partial content; CDN/cache serving a corrupted artifact.","solutions":["Retry the download (the retry loop already handles RequestException; hash mismatch should be retried too)","Verify the manifest URL and hash are from the same AGNOS release","Rule out intercepting proxies or modified TLS appliances on the network"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"# extend the existing retry loop to catch hash mismatches, not just RequestException\nexcept (requests.exceptions.RequestException, Exception) as e:\n    if 'hash mismatch' not in str(e):\n        raise\n    time.sleep(10)","preventionTips":["Ensure manifest and images come from the same release","Avoid intercepting proxies on the firmware download network"],"tags":["firmware","agnos","hash-mismatch","download"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}