{"record":{"id":"a1fd9bccebbc7692","repo":"commaai/openpilot","slug":"unhandled-sparse-chunk-type","errorCode":null,"errorMessage":"Unhandled sparse chunk type","messagePattern":"Unhandled sparse chunk type","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"critical","filePath":"openpilot/common/hardware/comma/agnos.py","lineNumber":85,"sourceCode":"  block_sz = struct.unpack(\"I\", f.read(4))[0]\n  f.read(4)  # total blocks\n  num_chunks = struct.unpack(\"I\", f.read(4))[0]\n  f.read(4)  # crc checksum\n\n  for _ in range(num_chunks):\n    chunk_type, out_blocks = SPARSE_CHUNK_FMT.unpack(f.read(12))\n\n    if chunk_type == 0xcac1:  # Raw\n      # TODO: yield in smaller chunks. Yielding only block_sz is too slow. Largest observed data chunk is 252 MB.\n      yield f.read(out_blocks * block_sz)\n    elif chunk_type == 0xcac2:  # Fill\n      filler = f.read(4) * (block_sz // 4)\n      for _ in range(out_blocks):\n        yield filler\n    elif chunk_type == 0xcac3:  # Don't care\n      yield b\"\"\n    else:\n      raise Exception(\"Unhandled sparse chunk type\")\n\n\n# noop wrapper with same API as unsparsify() for non sparse images\ndef noop(f: StreamingDecompressor) -> Generator[bytes, None, None]:\n  while len(chunk := f.read(1024 * 1024)) > 0:\n    yield chunk\n\n\ndef get_target_slot_number() -> int:\n  current_slot = subprocess.check_output([\"abctl\", \"--boot_slot\"], encoding='utf-8').strip()\n  return 1 if current_slot == \"_a\" else 0\n\n\ndef slot_number_to_suffix(slot_number: int) -> str:\n  assert slot_number in (0, 1)\n  return '_a' if slot_number == 0 else '_b'\n\n","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/common/hardware/comma/agnos.py#L67-L103","documentation":"unsparsify() walks Android sparse-image chunks and only understands types 0xcac1 (raw), 0xcac2 (fill), and 0xcac3 (don't-care). Any other chunk type in the header raises this generic Exception, meaning the downloaded image is not a sparse image this parser supports (or is corrupt).","triggerScenarios":"Flashing an AGNOS partition whose streamed image contains a chunk header with an unexpected type value — corrupt download, wrong image, or a new sparse format variant.","commonSituations":"Truncated/corrupted firmware download producing garbage chunk headers; flashing a non-sparse or newer-format image through the sparse path; endianness/sign issues in a hand-built image.","solutions":["Retry the AGNOS update — the image stream is likely corrupted in transit","Verify the manifest hash matches the image being downloaded","Confirm the image is an Android sparse image compatible with this parser"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    flash_agnos_update(manifest)\nexcept Exception as e:\n    if \"Unhandled sparse chunk type\" in str(e):\n        clear_download_cache_and_retry()","preventionTips":["Verify firmware image hashes before flashing","Use a reliable network for firmware downloads"],"tags":["firmware","agnos","sparse-image","flashing"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}