{"record":{"id":"53bc9a9a24863c5c","repo":"commaai/openpilot","slug":"sector-erase-verification-failed","errorCode":null,"errorMessage":"sector erase verification failed","messagePattern":"sector erase verification failed","errorType":"console","errorClass":"RuntimeError","httpStatus":null,"severity":"warning","filePath":"openpilot/system/hardware/chestnut/flash.py","lineNumber":312,"sourceCode":"      check_budget()\n      print(f\"{label} attempt {attempt}: {e}\", flush=True)\n      reconnect(flash)\n\n\ndef stable_read(flash, addr, length, count=2):\n  def read():\n    reads = [flash.read(addr, length) for _ in range(count)]\n    if any(x != reads[0] for x in reads[1:]):\n      raise RuntimeError(f\"unstable flash read at 0x{addr:05x}\")\n    return reads[0]\n  return with_retries(flash, f\"read 0x{addr:05x}\", read)\n\n\ndef program_sector(flash, addr, target):\n  def program():\n    flash.erase_sector(addr)\n    if flash.read(addr, SECTOR) != bytes([0xFF]) * SECTOR:\n      raise RuntimeError(\"sector erase verification failed\")\n    for off in range(0, SECTOR, PAGE):\n      chunk = target[off:off + PAGE]\n      if chunk != bytes([0xFF]) * len(chunk):\n        flash.program(addr + off, chunk)\n        if flash.read(addr + off, len(chunk)) != chunk:\n          raise RuntimeError(f\"page verify failed at 0x{addr + off:05x}\")\n    if flash.read(addr, SECTOR) != target:\n      raise RuntimeError(\"sector verification failed\")\n  with_retries(flash, f\"sector 0x{addr:05x}\", program)\n\n\ndef config_path():\n  return os.path.join(CONFIG_DIR, f\"{os.uname().nodename}.bin\")\n\n\ndef saved_config(path, data):\n  os.makedirs(os.path.dirname(path), exist_ok=True)\n  try:","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/system/hardware/chestnut/flash.py#L294-L330","documentation":"RuntimeError inside program_sector(): after erase_sector(addr), the tool reads the sector back and expects all 0xFF (the erased state of SPI NOR). Anything else means the erase did not actually take - block protection still set, worn-out flash cells, or unstable reads. Raised inside with_retries so the sector is retried after reconnect.","triggerScenarios":"program_sector() executes transaction(0x20, addr) then immediately flash.read(addr, SECTOR); if any of the 4096 bytes is not 0xFF this raises. Common when write_enable did not latch (erase silently ignored due to protection), the flash is worn out, or the read path is corrupt (compounding error 72).","commonSituations":"Block-protection bits not cleared (init() path failed or was skipped); a sector that exceeded its erase-cycle endurance and will not erase to 0xFF; marginal USB causing wrong read-back; wrong flash geometry assumptions.","solutions":["Retry via with_retries (automatic); a transient cause recovers on the second attempt","Re-run the full init() (clear block protection) if failures cluster - check that status() & 0x1C is 0 before erasing","Read the sector twice (stable_read) to rule out read instability before blaming the erase","If the same sector repeatedly fails to erase, the flash chip is worn - replace the enclosure; do not proceed with partial erases"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"def sector_erase_verified(flash, addr) -> bool:\n    try:\n        return flash.read(addr, 4096) == b'\\xff' * 4096\n    except Exception:\n        return False","typeGuard":null,"tryCatchPattern":"# program_sector already retries internally via with_retries\n# surface only persistent failures\ntry:\n    program_sector(flash, addr, target)\nexcept TimeoutError:\n    raise  # budget exhausted: stop, do not keep looping","preventionTips":["Check status() & 0x1C == 0 before starting sector programming","Use with_retries-wrapped program_sector rather than manual erase/program","Retire enclosures whose sectors repeatedly fail to erase"],"tags":["spi","erase","flash-write","retry","verify"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}