{"record":{"id":"66e3d228aec98c69","repo":"commaai/openpilot","slug":"flash-did-not-converge-within-flash-budget-g-s","errorCode":null,"errorMessage":"flash did not converge within {FLASH_BUDGET:g}s","messagePattern":"flash did not converge within (.+?)s","errorType":"console","errorClass":"TimeoutError","httpStatus":null,"severity":"critical","filePath":"openpilot/system/hardware/chestnut/flash.py","lineNumber":42,"sourceCode":"VBUS_PATH = \"/sys/kernel/debug/regulator/smb2-vbus/enable\"\nIMAGE_OFFSET = 0x100\nSECTOR, PAGE = 4096, 128\nMAX_CODE_SIZE = 0x10000\nFLASH_BUDGET = 600.0\nUSBDEVFS_CONTROL = 0xC0185500\nUSBDEVFS_BULK = 0xC0185502\nUSBDEVFS_SETINTERFACE = 0x80085504\nUSBDEVFS_SETCONFIGURATION = 0x80045505\nUSBDEVFS_CLAIMINTERFACE = 0x8004550F\nUSBDEVFS_RESET = 0x5514\nUSBDEVFS_CLEAR_HALT = 0x80045515\n\n_deadline = float(\"inf\")\n\n\ndef check_budget():\n  if time.monotonic() > _deadline:\n    raise TimeoutError(f\"flash did not converge within {FLASH_BUDGET:g}s\")\n\n\nclass Ctrl(ctypes.Structure):\n  _fields_ = [(\"request_type\", ctypes.c_uint8), (\"request\", ctypes.c_uint8),\n              (\"value\", ctypes.c_uint16), (\"index\", ctypes.c_uint16),\n              (\"length\", ctypes.c_uint16), (\"timeout\", ctypes.c_uint32),\n              (\"data\", ctypes.c_void_p)]\n\n\nclass Bulk(ctypes.Structure):\n  _fields_ = [(\"ep\", ctypes.c_uint), (\"len\", ctypes.c_uint),\n              (\"timeout\", ctypes.c_uint), (\"data\", ctypes.c_void_p)]\n\n\nclass RomFallback(Exception):\n  pass\n\n","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/system/hardware/chestnut/flash.py#L24-L60","documentation":"TimeoutError raised by check_budget() in the chestnut firmware flasher when time.monotonic() passes the global deadline set at flash start (FLASH_BUDGET = 600 s). The flasher polls the device through retry loops; each checkpoint calls check_budget so a stuck device cannot loop forever, and this error ends the attempt.","triggerScenarios":"The chestnut device stops responding (no USB re-enumeration after a reset, bulk transfers stalling) so erase/write/verify retries spin until the 600-second budget is exhausted; check_budget then raises at the next call site.","commonSituations":"Flaky USB cable/port causing stalled transfers, a device wedged in the ROM bootloader, or a hardware fault on the peripheral. Note the device may be left mid-flash (in ROM bootloader mode) — usually recoverable by rerunning.","solutions":["Simply rerun the flasher: a device in the ROM bootloader is normally recovered on the next attempt","Use a direct, short USB connection (no hubs) and a different port/cable","Check dmesg for USB errors (babble, stall, disconnect) during the failed attempt","Confirm only one chestnut device is attached (see find_chestnut)"],"exampleFix":"// before\nflash_chestnut()  # single attempt, wedges for 600s then times out\n\n// after\nfor attempt in range(3):\n  try:\n    flash_chestnut()\n    break\n  except TimeoutError:\n    print(f'flash attempt {attempt} timed out, retrying')","handlingStrategy":"retry","validationCode":"import time\n\ndef budget_remaining() -> float:\n    from openpilot.system.hardware.chestnut import flash\n    return max(0.0, flash._deadline - time.monotonic())","typeGuard":null,"tryCatchPattern":"for attempt in range(3):\n    try:\n        flash_chestnut()\n        break\n    except TimeoutError:\n        print(f'flash timed out on attempt {attempt}; device likely in ROM bootloader, retrying')","preventionTips":["Flash over a short, direct USB cable on a rear/root-hub port","Leave only the target device connected","Expect ROM-bootloader recovery: a timed-out device is normally reflashed successfully on rerun"],"tags":["firmware","flashing","usb","hardware","timeout"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}