diff --git a/crates/fbuild-daemon/src/handlers/operations/deploy.rs b/crates/fbuild-daemon/src/handlers/operations/deploy.rs index 0ce7d646..984a5d1d 100644 --- a/crates/fbuild-daemon/src/handlers/operations/deploy.rs +++ b/crates/fbuild-daemon/src/handlers/operations/deploy.rs @@ -1228,6 +1228,19 @@ pub async fn deploy( }; ctx.serial_manager.detach_reader(&monitor_port, &request_id); + // Detaching the reader alone leaves the session -- and its OS serial + // handle -- open forever, so the next client gets EBUSY on a board + // that is enumerated and perfectly healthy. The monitor and WebSocket + // cleanup paths both schedule the physical close here; this one did + // not, which is why the leak only showed up after a deploy. + // See FastLED/fbuild#1426. + if !ctx.serial_manager.has_clients(&monitor_port) { + ctx.serial_manager.close_port_after_grace_if_idle( + &monitor_port, + &request_id, + std::time::Duration::from_secs(2), + ); + } return match monitor_result { MonitorOutcome::Success(msg) => (