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

  1. Avoid racing shutdown with connect: join or abort outstanding connect tasks before calling shutdown.
  2. In callers, treat this error as benign cancellation (log debug; do not retry or alert).
  3. In reconnect loops, check the shutdown flag each iteration before reconnecting.
  4. 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

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/3bd20898203845b8. Report an issue: GitHub.