{"record":{"id":"aab06168faec4ffd","repo":"commaai/openpilot","slug":"invalid-config-backup-path","errorCode":null,"errorMessage":"invalid config backup: {path}","messagePattern":"invalid config backup: (.+?)","errorType":"console","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"openpilot/system/hardware/chestnut/flash.py","lineNumber":335,"sourceCode":"        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:\n      print(f\"restoring config from {path}\", flush=True)\n    return backup\n  with os.fdopen(fd, \"wb\") as f:\n    f.write(data)\n    f.flush()\n    os.fsync(f.fileno())\n  return data\n\n\ndef rom_write(image, config):\n  # the ROM bootloader implements only the BOT protocol, and requires a port reset before bulk transfers\n  path, _, _ = find_chestnut()\n  if path is None:\n    raise RuntimeError(\"chestnut disappeared before recovery\")\n  unbind_drivers(path)\n  fd = open_device(path)\n  try:","sourceCodeStart":317,"sourceCodeEnd":353,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/system/hardware/chestnut/flash.py#L317-L353","documentation":"RuntimeError from saved_config(): a config backup file already exists at CONFIG_DIR/<hostname>.bin (the O_EXCL open failed with FileExistsError), but its size is not exactly 0x100 (256) bytes - the size of one config page. The tool keeps a host-local backup of the device's config sector so a reflash can restore it; a malformed backup cannot be trusted and is rejected instead of silently used.","triggerScenarios":"saved_config(path, data) when /data/chestnut_config/<nodename>.bin exists and len(backup) != 256: a previous run was interrupted mid-write, the file was manually edited or truncated, or a different format version wrote it.","commonSituations":"Interrupted earlier flash leaving a short file; disk-full during the original backup; someone copied a config from another tool; a hostname reused across hardware generations with different config sizes.","solutions":["Inspect the file with ls -l /data/chestnut_config/<hostname>.bin - anything not 256 bytes is not a valid config page","If you accept losing the old backup, move it aside (mv file file.bak) and re-run; the tool will write a fresh backup from the device's current config","Verify the current on-device config reads back stably (stable_read at 0x100) before letting it become the new backup","Preserve valid 256-byte backups carefully - they are the only recovery source for device-specific config after a bad flash"],"exampleFix":"# recover from malformed backup\nmv /data/chestnut_config/$(hostname).bin /data/chestnut_config/$(hostname).bin.corrupt\n# re-run flasher to create a fresh backup","handlingStrategy":"validation","validationCode":"import os\n\ndef backup_valid(path) -> bool:\n    return not os.path.exists(path) or (os.path.getsize(path) == 0x100)","typeGuard":null,"tryCatchPattern":"try:\n    cfg = saved_config(path, data)\nexcept RuntimeError as e:\n    if 'invalid config backup' in str(e):\n        archive_and_regenerate(path)  # mv aside, re-run for fresh backup","preventionTips":["Never hand-edit config backup files","Keep /data from filling up - partial writes follow full disks","Snapshot the config backup directory before flashing campaigns"],"tags":["config","backup","file-size","state"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}