{"record":{"id":"6d2960137c557f9d","repo":"commaai/openpilot","slug":"page-verify-failed-at-0x-addr-off-05x","errorCode":null,"errorMessage":"page verify failed at 0x{addr + off:05x}","messagePattern":"page verify failed at 0x(.+?)","errorType":"console","errorClass":"RuntimeError","httpStatus":null,"severity":"warning","filePath":"openpilot/system/hardware/chestnut/flash.py","lineNumber":318,"sourceCode":"  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:\n    fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)\n  except FileExistsError as e:\n    backup = open(path, \"rb\").read()\n    if len(backup) != 0x100:\n      raise RuntimeError(f\"invalid config backup: {path}\") from e\n    if backup != data:","sourceCodeStart":300,"sourceCodeEnd":336,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/system/hardware/chestnut/flash.py#L300-L336","documentation":"RuntimeError inside program_sector(): after flash.program(addr+off, chunk) wrote one 128-byte page, the immediate read-back did not match the data written. The page program either did not commit (write protection, WEL dropped), committed partially, or the read-back is corrupt. Raised inside with_retries, so the whole sector is erased and retried cleanly.","triggerScenarios":"program_sector()'s per-page loop: flash.program() does write_buffer + write_enable + transaction(0x02, addr, len, mode=1) + wait_write_done, then flash.read(addr+off, len(chunk)) != chunk triggers this. One flipped bit in read-back is enough.","commonSituations":"Marginal USB link corrupting either the write payload or the verify read; block protection re-asserting; a weak flash cell in that page; controller register state lost between write and verify due to an implicit device reset.","solutions":["Rely on with_retries: the sector is re-erased and rewritten from the source data, which fixes transient bit errors","If pages fail consistently at the same address, map it: a repeat offender is a worn page - replace the hardware","Verify link stability first with stable_read on an untouched region; if reads alone are unstable, fix the cable/port before blaming the flash","Confirm init() ran on this connection so write-enable actually latches"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    program_sector(flash, addr, target)\nexcept TimeoutError:\n    raise  # FLASH_BUDGET exhausted - persistent page failures elsewhere","preventionTips":["Keep every page write inside the verified program_sector flow","Verify link stability (stable_read on an untouched area) before programming","Replace hardware where the same page address fails across sessions"],"tags":["spi","page-program","flash-write","retry","verify"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}