{"record":{"id":"02637183039f6258","repo":"commaai/openpilot","slug":"invalid-config-backup-backup","errorCode":null,"errorMessage":"invalid config backup: {backup}","messagePattern":"invalid config backup: (.+?)","errorType":"console","errorClass":"RuntimeError","httpStatus":null,"severity":"critical","filePath":"openpilot/system/hardware/chestnut/flash.py","lineNumber":494,"sourceCode":"    if in_rom_bootloader(vid_pid, product):\n      if not recover_from_rom(image, expected_product):\n        return\n      # firmware is back, verify it against the bundled image\n      force, product = True, None\n    write_image(image, expected_product, product, force)\n  finally:\n    for sig, handler in previous.items():\n      signal.signal(sig, handler)\n\n\ndef recover_from_rom(image, expected_product):\n  # returns whether the chestnut came back on custom firmware\n  backup = config_path()\n  if not os.path.isfile(backup):\n    raise RuntimeError(f\"cannot recover from the ROM bootloader without a config backup at {backup}\")\n  config = open(backup, \"rb\").read()\n  if len(config) != 0x100:\n    raise RuntimeError(f\"invalid config backup: {backup}\")\n\n  committed = False\n  while True:\n    check_budget()\n    path, vid_pid, product = find_chestnut()\n    if path is None:\n      if committed:\n        print(\"chestnut is offline, recovered firmware boots on its next power cycle\", flush=True)\n        return False\n      vbus_cycle()\n      continue\n    if not in_rom_bootloader(vid_pid, product):\n      return True\n    if committed:\n      print(\"chestnut stayed powered, recovered firmware boots on its next power cycle\", flush=True)\n      return False\n    try:\n      rom_write(image, config)","sourceCodeStart":476,"sourceCodeEnd":512,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/system/hardware/chestnut/flash.py#L476-L512","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import os\nbackup = config_path()\nif os.path.isfile(backup):\n    size = os.path.getsize(backup)\n    if size != 0x100:\n        raise SystemExit(f\"config backup is {size} bytes, expected 256 - restore a known-good copy before recovery\")","typeGuard":null,"tryCatchPattern":"try:\n    config = open(backup, 'rb').read()\nexcept RuntimeError as e:\n    if 'invalid config backup' in str(e):\n        raise SystemExit(\"restore a 256-byte backup from version control before retrying\")\n    raise","preventionTips":["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"],"tags":["firmware","recovery","corruption","validation","openpilot"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}