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
- Register an RP2040 device (e.g. BTT Eddy USB) with
is_katapult: false, method serial
- Flash firmware that changes the USB manufacturer string (e.g. custom Klipper fork with a different vendor name)
- First flash succeeds — device now enumerates with a new
/dev/serial/by-id/ path
- 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
- 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.
- If
make flash exits non-zero or the device doesn't re-enumerate within a timeout, report the failure rather than returning success.
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 thedevice_idinfleet.jsonbecomes 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
is_katapult: false, methodserial/dev/serial/by-id/pathExpected behavior
fleet.jsonif the path changed (matching by chip serial suffix)make flashfails or the device doesn't re-enumerate after flashing, the error is reported to the userActual behavior
POST /flashreturns 200 OKrp2040_flashcommand executes (confirmed viajournalctl -u klipperfleet)lsusbshows2e8a:0003 Raspberry Pi RP2 Boot)mcu 'eddy': Unable to connect)Environment
be1d71e)27902226cab6)is_katapult: false, method:serialSuggested fix
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, updatefleet.jsonautomatically.make flashexits non-zero or the device doesn't re-enumerate within a timeout, report the failure rather than returning success.