nautechsystems/nautilus_trader · warning
RTDS connection was canceled by shutdown
Error message
RTDS connection was canceled by shutdown
What it means
Raised when a freshly established RTDS WebSocket connection belongs to a superseded shutdown generation — shutdown was requested while the WebSocket handshake was in flight. The code notifies closed, disconnects the new socket, and bails so the stale connection is never registered. It is a deliberate cancellation guard, not a connection failure.
Source
Thrown at crates/adapters/polymarket/src/rtds.rs:876
WebSocketClient::builder()
.config(config)
.message_handler(handler)
.maybe_state_sink(
self.inner
.socket_control
.as_ref()
.map(SocketControl::sink)
.or_else(|| self.inner.socket_sink.clone()),
)
.connect()
.await
.context("failed to connect Polymarket RTDS WebSocket")?,
);
if !self.is_generation_open(generation) {
ws.notify_closed();
ws.disconnect().await;
anyhow::bail!("RTDS connection was canceled by shutdown");
}
log::debug!("Polymarket RTDS WebSocket connected: {}", self.inner.url);
*self.inner.ws_client.lock() = Some(Arc::clone(&ws));
// Tokio cancellation is cooperative. Quiesce the previous loop before
// activating the replacement so an admitted old-loop tail cannot emit
// after newer data from the new connection.
let tasks = self
.task_slots()
.ok_or_else(|| anyhow::anyhow!("RTDS task owner was dropped"))?;
let mut message_slot = tasks.message.lock().await;
if !self.is_generation_open(generation) {
drop(message_slot);
ws.notify_closed();
ws.disconnect().await;
self.clear_ws_if_current(&ws);
anyhow::bail!("RTDS connection was canceled by shutdown");View on GitHub (pinned to 18893faf8b)
Solutions
- Avoid racing shutdown with connect: join or abort outstanding connect tasks before calling shutdown.
- In callers, treat this error as benign cancellation (log debug; do not retry or alert).
- In reconnect loops, check the shutdown flag each iteration before reconnecting.
- If it fires without an actual shutdown, verify the generation counter is not being bumped erroneously.
Example fix
// before handle.abort(); // shutdown while connect in flight -> canceled error feed.disconnect().await; // after feed.disconnect().await; // cancels/joins in-flight connect cleanly first handle.abort();
Defensive patterns
Strategy: retry
Validate before calling
if feed.is_shutdown() {
log::debug!("not reconnecting: feed shut down");
return Ok(());
} Type guard
fn is_shutdown_cancellation(err: &anyhow::Error) -> bool {
err.to_string().contains("canceled by shutdown")
} Try / catch
match feed.ensure_connected().await {
Ok(()) => {},
Err(e) if is_shutdown_cancellation(&e) => log::debug!("reconnect canceled by shutdown"),
Err(e) => return Err(e),
} Prevention
- Stop reconnect loops when shutdown is signaled (select! on a shutdown token).
- Abort reconnection tasks before calling disconnect/shutdown.
- Filter this error out of retry counters — retrying after shutdown is pointless.
- Keep generation checks centralized so the guard stays consistent.
When it happens
Trigger: disconnect/shutdown bumps the generation (or sets closing) while ensure_connected's WebSocket connect is awaiting; the connect completes after the generation check window and is rejected.
Common situations: Engine shutdown racing a reconnect loop; test teardown aborting tasks mid-connect; user calling disconnect concurrently with automatic reconnection after a network drop.
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
- Polymarket data shutdown failed: {}
- RTDS task owner was dropped
- Polymarket RTDS task owner was dropped
- Polymarket market pool shutdown failed: {}
- {e}; subscription rollback failed: {close_error}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/3bd20898203845b8.
Report an issue: GitHub.