nautechsystems/nautilus_trader · error
Failed to start Lighter WebSocket handler task: {e}
Error message
Failed to start Lighter WebSocket handler task: {e} What it means
Raised when spawning the Lighter WebSocket handler tokio task fails. The code resets self.out_rx to None and bails with the JoinHandle error from tokio::spawn. On modern tokio this almost always means the runtime is shutting down or spawn was called outside a runtime context.
Source
Thrown at crates/adapters/lighter/src/websocket/client.rs:627
log::error!("Failed to send Lighter message (receiver dropped)");
}
break;
}
}
None => {
if handler.is_stopped() {
log::debug!("Lighter handler stop signal observed, exiting loop");
break;
}
log::warn!("Lighter WebSocket stream ended unexpectedly");
break;
}
}
}
log::debug!("Lighter handler task completed");
}) {
self.out_rx = None;
anyhow::bail!("Failed to start Lighter WebSocket handler task: {e}");
}
Ok(())
}
/// Disconnects gracefully: signals shutdown, drains the handler, then
/// awaits the task handle with a timeout.
///
/// # Errors
///
/// This function currently completes best-effort shutdown and returns `Ok(())`.
pub async fn disconnect(&mut self) -> Result<(), LighterWsError> {
self.connection_generation.fetch_add(1, Ordering::AcqRel);
self.initial_connect_cancellation.load().cancel();
let _guard = self.connection_lock.lock().await;
self.initial_connect_cancellation.load().cancel();
log::debug!("Disconnecting Lighter WebSocket");View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure connect() is awaited within a live tokio runtime (use #[tokio::main] or an explicit Runtime/Handle).
- If using a custom runtime, pass its Handle so tokio::spawn can find an executor.
- Delay client startup until after the runtime is initialized; don't spawn clients during shutdown.
- Check that node start ordering doesn't stop the runtime before WebSocket clients connect.
- Wrap startup in a runtime::Handle::current().spawn if bridging from another executor.
Example fix
// before: calling connect outside a runtime
std::thread::spawn(|| client.connect().await?); // spawn fails
// after: run inside the tokio runtime
let handle = tokio::runtime::Handle::current();
handle.spawn(async move { client.connect().await })?; Defensive patterns
Strategy: try-catch
Validate before calling
// ensure a runtime exists before connecting let _guard = tokio::runtime::Handle::try_current()?;
Try / catch
match client.connect().await {
Err(e) if e.to_string().contains("Failed to start") => {
log::error!("runtime unavailable for handler spawn: {e:#}");
// re-run startup inside an active runtime handle
}
r => r?,
} Prevention
- Always call connect() within a live tokio runtime
- Don't initialize clients during runtime shutdown
- Pass an explicit Handle when bridging from other executors
- Verify node start ordering: runtime first, then clients
When it happens
Trigger: Calling connect() outside a tokio runtime, or during runtime shutdown, so tokio::spawn for the handler task returns an error which is surfaced with this message.
Common situations: Calling client.connect() from a blocking thread or plain async fn without #[tokio::main]/runtime handle; stopping the actor/runtime while a data client is still initializing; embedding the adapter in a non-tokio executor.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
Related errors
- task slot is already occupied
- Failed to start Coinbase WebSocket handler task: {e}
- Failed to start Hyperliquid WebSocket handler task: {e}
- Failed to create tokio runtime
- failed to finish Binance Futures data command tasks: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/445d7020fddc3fc9.
Report an issue: GitHub.