Skip to content

Device ID becomes stale after flash if USB descriptor changes #17

@bassco

Description

@bassco

Summary

After flashing a non-Katapult RP2040 device (method: serial, is_katapult: false), the USB serial-by-id path can change if the firmware alters the USB manufacturer descriptor. KlipperFleet doesn't rescan /dev/serial/by-id/ after flashing, so the device_id in fleet.json becomes stale. Subsequent flash attempts silently fail — the device enters USB bootloader but the firmware write never executes, leaving it bricked in RP2 Boot mode.

Steps to reproduce

  1. Register an RP2040 device (e.g. BTT Eddy USB) with is_katapult: false, method serial
  2. Flash firmware that changes the USB manufacturer string (e.g. custom Klipper fork with a different vendor name)
  3. First flash succeeds — device now enumerates with a new /dev/serial/by-id/ path
  4. Attempt a second flash via KlipperFleet

Expected behavior

  • KlipperFleet rescans serial devices after a successful flash and updates fleet.json if the path changed (matching by chip serial suffix)
  • If make flash fails or the device doesn't re-enumerate after flashing, the error is reported to the user

Actual behavior

  • POST /flash returns 200 OK
  • Services are stopped, but no rp2040_flash command executes (confirmed via journalctl -u klipperfleet)
  • Device is left stuck in RP2 Boot mode (lsusb shows 2e8a:0003 Raspberry Pi RP2 Boot)
  • Services are restarted, Klipper enters error state (mcu 'eddy': Unable to connect)
  • No error is surfaced to the user

Environment

  • KlipperFleet v1.2.0-alpha (be1d71e)
  • Kalico v2026.03.00 (27902226cab6)
  • Hardware: BTT Eddy USB (RP2040), is_katapult: false, method: serial
  • Raspberry Pi 4

Suggested fix

  1. After a successful make flash, rescan /dev/serial/by-id/ and match devices by their unique chip serial (the suffix portion of the path, e.g. rp2040_50443403106DC01C). If the full path changed, update fleet.json automatically.
  2. If make flash exits non-zero or the device doesn't re-enumerate within a timeout, report the failure rather than returning success.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions