turbo
b0d2f627 - fix: Prevent turbo dev from hanging when daemon file watching fails (#12091)

Commit
243 days ago
fix: Prevent turbo dev from hanging when daemon file watching fails (#12091) ## Summary - Add timeouts to prevent `turbo dev` from hanging indefinitely when the daemon's file watcher fails to initialize - Fix the daemon's `package_changes` gRPC stream to properly close when the underlying broadcast channel is closed - Improve error messages to help users diagnose and recover from file watching failures ## Context Users upgrading from 2.8.10 to 2.8.11/2.8.12 reported that `turbo dev` hangs after printing the version number, showing no output and no error. The root cause is a chain of missing timeouts and error handling in the daemon's file watching pipeline. When the daemon's `FileSystemWatcher` fails to initialize (e.g. FSEvents on macOS doesn't deliver events, cookie directory permission issues, or a stale daemon from a previous version), several things go wrong: 1. The `PackageChangesWatcher` subscriber waits forever on `OptionalWatch::get()` for file events that will never arrive 2. The daemon's gRPC `package_changes` stream handler loops on `RecvError::Closed` instead of breaking, producing infinite error events 3. The client's `WatchClient::start()` blocks on `events.next().await` with no timeout, appearing frozen ## Changes **`crates/turborepo-lib/src/run/watch.rs`** — Add a 10-second timeout when the client waits for the first event from the daemon's `package_changes` stream. Fails with an actionable error message instead of hanging. **`crates/turborepo-lib/src/package_changes_watcher.rs`** — Add a 5-second timeout to the subscriber's wait for file events to become ready. Previously waited indefinitely. **`crates/turborepo-daemon/src/server.rs`** — Fix the `package_changes` gRPC handler to distinguish `RecvError::Closed` from `RecvError::Lagged`. On `Closed`, it now sends one error event and breaks out of the loop instead of spinning. **`crates/turborepo-filewatch/src/lib.rs`** — Upgrade file watcher initialization failure logs from `warn!` to `error!` with guidance to run `turbo daemon clean`. ## Testing Added 3 new tests: - `optional_watch::test::get_returns_error_when_sender_dropped` — Verifies error propagation when the sender drops without sending - `optional_watch::test::get_with_timeout_returns_elapsed_when_no_value` — Verifies timeout behavior for the pattern used in the fix - `server::test::package_changes_stream_closes_on_sender_drop` — Reproduces the exact bug: broadcast sender drops, verifies the forwarding loop sends an error event and terminates instead of spinning
Author
Parents
Loading