commaai/openpilot · warning · RuntimeError
page verify failed at 0x{addr + off:05x}
Error message
page verify failed at 0x{addr + off:05x} What it means
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.
Source
Thrown at openpilot/system/hardware/chestnut/flash.py:318
def read():
reads = [flash.read(addr, length) for _ in range(count)]
if any(x != reads[0] for x in reads[1:]):
raise RuntimeError(f"unstable flash read at 0x{addr:05x}")
return reads[0]
return with_retries(flash, f"read 0x{addr:05x}", read)
def program_sector(flash, addr, target):
def program():
flash.erase_sector(addr)
if flash.read(addr, SECTOR) != bytes([0xFF]) * SECTOR:
raise RuntimeError("sector erase verification failed")
for off in range(0, SECTOR, PAGE):
chunk = target[off:off + PAGE]
if chunk != bytes([0xFF]) * len(chunk):
flash.program(addr + off, chunk)
if flash.read(addr + off, len(chunk)) != chunk:
raise RuntimeError(f"page verify failed at 0x{addr + off:05x}")
if flash.read(addr, SECTOR) != target:
raise RuntimeError("sector verification failed")
with_retries(flash, f"sector 0x{addr:05x}", program)
def config_path():
return os.path.join(CONFIG_DIR, f"{os.uname().nodename}.bin")
def saved_config(path, data):
os.makedirs(os.path.dirname(path), exist_ok=True)
try:
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
except FileExistsError as e:
backup = open(path, "rb").read()
if len(backup) != 0x100:
raise RuntimeError(f"invalid config backup: {path}") from e
if backup != data:View on GitHub (pinned to 516ec1e682)
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
Defensive patterns
Strategy: retry
Try / catch
try:
program_sector(flash, addr, target)
except TimeoutError:
raise # FLASH_BUDGET exhausted - persistent page failures elsewhere Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- sector erase verification failed
- sector verification failed
- SPI flash WIP timeout
- unstable flash read at 0x{addr:05x}
- {func.__name__} failed after retry
AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15).
Data as JSON: /api/errors/6d2960137c557f9d.
Report an issue: GitHub.