nautechsystems/nautilus_trader · warning
Lighter WebSocket initial connection cancelled
Error message
Lighter WebSocket initial connection cancelled
What it means
Raised when the initial Lighter WebSocket connection succeeded, but by the time it finished the cancellation token was cancelled or the connection generation advanced (a newer connect attempt superseded this one). The client is disconnected and the stale attempt is aborted with this error instead of returning a connection that nobody owns.
Source
Thrown at crates/adapters/lighter/src/websocket/client.rs:498
.map(SocketControl::sink)
.or_else(|| self.socket_sink.clone()),
)
.connect();
let client =
match tokio::time::timeout(Duration::from_secs(self.ws_timeout_secs), connect).await {
Ok(result) => result?,
Err(_) => anyhow::bail!(
"Lighter WebSocket initial connection timeout after {} seconds",
self.ws_timeout_secs,
),
};
if cancellation_token.is_cancelled()
|| generation != self.connection_generation.load(Ordering::Acquire)
{
client.disconnect().await;
anyhow::bail!("Lighter WebSocket initial connection cancelled");
}
let (cmd_tx, cmd_rx) = tokio::sync::mpsc::unbounded_channel::<HandlerCommand>();
let (out_tx, out_rx) = tokio::sync::mpsc::unbounded_channel::<NautilusWsMessage>();
// Capture the connection-mode atomic before moving `client` into the
// SetClient command below.
let connection_mode_atomic = client.connection_mode_atomic();
let connection_epoch_atomic = client.connection_epoch_atomic();
// Queue SetClient (and the instrument cache replay) onto the new
// command channel BEFORE publishing it to clones or marking the
// connection active. Otherwise a clone observing `is_active()` could
// race in and send a Subscribe before SetClient lands, and the
// handler would drop the subscription because `inner == None`.
let reconnect_handle = client.reconnect_handle();
if let Err(e) = cmd_tx.send(HandlerCommand::SetClient(client)) {
anyhow::bail!("Failed to send SetClient command: {e}");View on GitHub (pinned to 18893faf8b)
Solutions
- If intentional shutdown: this is expected; handle the error as a benign cancellation rather than a fault.
- Avoid concurrent connect() calls on the same client; await the first connect before triggering a reconnect.
- Check whether something cancels the token early (e.g. a shutdown signal firing during startup) and fix the shutdown ordering.
- If reconnect storms occur, add backoff between reconnect attempts so generations don't race.
- Upgrade the adapter if a known race in generation handling causes spurious cancellations.
Example fix
// before: cancelling while connect is in flight let handle = tokio::spawn(client.connect()); token.cancel(); // connect will bail: initial connection cancelled let client = handle.await??; // after: connect first, then allow cancellation client.connect().await?; token.cancel(); // now shutdown is orderly
Defensive patterns
Strategy: try-catch
Try / catch
match client.connect().await {
Err(e) if e.to_string().contains("initial connection cancelled") => {
log::info!("connect superseded or cancelled; treating as benign");
}
r => r?,
} Prevention
- Don't cancel the token while connect() is in flight unless shutting down
- Serialize connect/reconnect calls; await one before triggering another
- Check shutdown ordering so tokens aren't cancelled during startup
- Treat this error as benign during intentional shutdown
When it happens
Trigger: Calling connect() and cancelling the CancellationToken while the handshake is in flight, or calling connect()/reconnect concurrently such that connection_generation increments before the first attempt finishes — the losing attempt bails here.
Common situations: Shutting down a node or data engine while the WebSocket client is still connecting; rapid disconnect/reconnect cycles (e.g. stream URL hot-reload) racing an in-progress connect; a caller dropping the connect future and retrying.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Modify order failed: {e}
- Cancel order failed: {e}
- Failed to send SetClient command: {e}
- Failed to start Hyperliquid WebSocket handler task: {e}
- Hyperliquid WebSocket handler failed: {error}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/24c726e1db40fced.
Report an issue: GitHub.