commaai/openpilot · critical · RuntimeError
invalid config backup: {backup}
Error message
invalid config backup: {backup} What it means
recover_from_rom() found the config backup file but its size is not exactly 0x100 (256) bytes, so it refuses to use it. The chestnut config region is a fixed 256-byte structure; a truncated, padded, or corrupted backup cannot be written back safely. This guards against restoring garbage into the device's config flash.
Source
Thrown at openpilot/system/hardware/chestnut/flash.py:494
if in_rom_bootloader(vid_pid, product):
if not recover_from_rom(image, expected_product):
return
# firmware is back, verify it against the bundled image
force, product = True, None
write_image(image, expected_product, product, force)
finally:
for sig, handler in previous.items():
signal.signal(sig, handler)
def recover_from_rom(image, expected_product):
# returns whether the chestnut came back on custom firmware
backup = config_path()
if not os.path.isfile(backup):
raise RuntimeError(f"cannot recover from the ROM bootloader without a config backup at {backup}")
config = open(backup, "rb").read()
if len(config) != 0x100:
raise RuntimeError(f"invalid config backup: {backup}")
committed = False
while True:
check_budget()
path, vid_pid, product = find_chestnut()
if path is None:
if committed:
print("chestnut is offline, recovered firmware boots on its next power cycle", flush=True)
return False
vbus_cycle()
continue
if not in_rom_bootloader(vid_pid, product):
return True
if committed:
print("chestnut stayed powered, recovered firmware boots on its next power cycle", flush=True)
return False
try:
rom_write(image, config)View on GitHub (pinned to 516ec1e682)
Solutions
- Check the file: ls -l <config_path()> - anything other than exactly 256 bytes is corrupt
- Restore the backup from a known-good copy (version control, another machine) and confirm it is 256 bytes before retrying
- If no intact copy exists, fall back to the official comma recovery procedure - do not pad or trim the file to force it to 256 bytes
Defensive patterns
Strategy: validation
Validate before calling
import os
backup = config_path()
if os.path.isfile(backup):
size = os.path.getsize(backup)
if size != 0x100:
raise SystemExit(f"config backup is {size} bytes, expected 256 - restore a known-good copy before recovery") Try / catch
try:
config = open(backup, 'rb').read()
except RuntimeError as e:
if 'invalid config backup' in str(e):
raise SystemExit("restore a 256-byte backup from version control before retrying")
raise Prevention
- Store config backups in version control or a checksummed archive (sha256 alongside the file) so integrity is verifiable
- Never transfer backups in text mode (git autocrlf, text-mode FTP) - they are fixed-size binary blobs
- After each successful flash, verify os.path.getsize(config_path()) == 256
When it happens
Trigger: The file at config_path() was truncated by a crash during the original backup write; someone hand-edited or regenerated the file; a text-mode transfer (FTP/git autocrlf) altered the file length.
Common situations: Backup file corrupted by an interrupted write; file transferred between machines in a way that changed its size; accidentally pointing config_path() at an unrelated 256-byte-unrelated file.
Related errors
- invalid wrapped firmware length or magic
- invalid wrapped firmware checksum
- invalid wrapped firmware CRC
- cannot recover from the ROM bootloader without a config back
- expected one chestnut, found {len(found)}
AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15).
Data as JSON: /api/errors/02637183039f6258.
Report an issue: GitHub.