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

  1. Check the file: ls -l <config_path()> - anything other than exactly 256 bytes is corrupt
  2. Restore the backup from a known-good copy (version control, another machine) and confirm it is 256 bytes before retrying
  3. 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

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


AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15). Data as JSON: /api/errors/02637183039f6258. Report an issue: GitHub.