{"record":{"id":"f71fe0564cb9a695","repo":"commaai/openpilot","slug":"chestnut-did-not-re-enumerate-after-reset","errorCode":null,"errorMessage":"chestnut did not re-enumerate after reset","messagePattern":"chestnut did not re-enumerate after reset","errorType":"console","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"openpilot/system/hardware/chestnut/flash.py","lineNumber":360,"sourceCode":"    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:\n    fcntl.ioctl(fd, USBDEVFS_RESET)\n  finally:\n    os.close(fd)\n  time.sleep(3)\n  path, _, _ = find_chestnut()\n  if path is None:\n    raise RuntimeError(\"chestnut did not re-enumerate after reset\")\n  fd = claim_interface(path, setup=True)\n  for ep in (0x02, 0x81):\n    fcntl.ioctl(fd, USBDEVFS_CLEAR_HALT, struct.pack(\"I\", ep))\n  tag = 0\n\n  def bulk(ep, payload, timeout):\n    buf = ctypes.create_string_buffer(bytes(payload), len(payload))\n    fcntl.ioctl(fd, USBDEVFS_BULK, Bulk(ep, len(payload), timeout, ctypes.cast(buf, ctypes.c_void_p)))\n    return buf.raw\n\n  def cmd(cdb, data=b\"\", timeout=30000):\n    nonlocal tag\n    tag += 1\n    bulk(0x02, struct.pack(\"<IIIBBB16s\", 0x43425355, tag, len(data), 0, 0, len(cdb), cdb), timeout)\n    if data:\n      bulk(0x02, data, timeout)\n    try:\n      csw = bulk(0x81, bytes(13), timeout)","sourceCodeStart":342,"sourceCodeEnd":378,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/system/hardware/chestnut/flash.py#L342-L378","documentation":"RuntimeError in rom_write(): after issuing USBDEVFS_RESET on the ROM bootloader device and sleeping 3s, find_chestnut() still finds no device. The ROM bootloader requires a port reset before bulk transfers; the expected flow is reset, re-enumeration, then claim with SETCONFIGURATION. If nothing re-appears within 3 seconds, the post-reset re-enumeration failed.","triggerScenarios":"rom_write() executes ioctl(fd, USBDEVFS_RESET), closes the fd, sleeps 3s, rescans: None means raise. Happens when the reset power-cycles the device into a state that does not re-enumerate (dead flash config, power problem), when 3s is too short for a slow device/host combination, or when the reset made the host re-bind a driver that hides the device.","commonSituations":"Slow hubs or controllers handling USBDEVFS_RESET poorly; device drawing power renegotiation after reset; insufficient bus power; a device that only comes back after a full power cycle.","solutions":["Wait a few seconds and check lsusb manually - if the device shows up late, re-run; consider increasing the sleep locally for slow hosts","Check that nothing (usb-storage) grabbed the re-enumerated ROM interface before the next claim - unbind if needed","Try a different USB port, direct connection preferred; some hubs handle USBDEVFS_RESET poorly","If the device never comes back after reset, power-cycle the enclosure physically and start recovery over; repeated failure suggests hardware fault or insufficient power"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"import time\nfrom flash import find_chestnut\n\ndef waits_for_renumeration(seconds=10) -> bool:\n    end = time.monotonic() + seconds\n    while time.monotonic() < end:\n        if find_chestnut()[0] is not None:\n            return True\n        time.sleep(0.5)\n    return False","typeGuard":null,"tryCatchPattern":"try:\n    rom_write(image, config)\nexcept RuntimeError as e:\n    if 're-enumerate' in str(e):\n        if waits_for_renumeration(15):\n            rom_write(image, config)\n        else:\n            power_cycle_enclosure()","preventionTips":["Use ports/controllers known to handle USBDEVFS_RESET cleanly (direct, not hub)","Give slow devices time after reset before assuming failure","Power-cycle the enclosure as the standard reset-retry escalation"],"tags":["usb","port-reset","recovery","bootloader","enumeration"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}