nautechsystems/nautilus_trader · error

Cannot connect while previous WebSocket handler task is stil

Error message

Cannot connect while previous WebSocket handler task is still running

What it means

`connect` enforces that no previous WebSocket handler task is still alive before starting a new connection. If a prior handler task exists and has not finished (still connected/subscribed), starting another would double-handle messages, so the call fails with this error.

Source

Thrown at crates/adapters/okx/src/websocket/client.rs:600

    }

    /// Gets the current VIP level.
    pub fn vip_level(&self) -> OKXVipLevel {
        let level = self.vip_level.load(Ordering::Relaxed);
        OKXVipLevel::from(level)
    }

    /// Connect to the OKX WebSocket server.
    ///
    /// # Errors
    ///
    /// Returns an error if the connection process fails.
    pub async fn connect(&mut self) -> anyhow::Result<()> {
        let connect_lock = Arc::clone(&self.connect_lock);
        let _connect_guard = connect_lock.lock().await;

        if !self.handler_tasks.is_empty() && !self.handler_tasks.all_finished() {
            anyhow::bail!("Cannot connect while previous WebSocket handler task is still running");
        }

        if !self.handler_tasks.is_open() || !self.handler_tasks.is_empty() {
            self.handler_tasks.begin_shutdown();
            self.handler_tasks
                .finish_shutdown(Duration::from_secs(2), Duration::from_secs(2))
                .await
                .map_err(|e| anyhow::anyhow!("Previous WebSocket handler failed: {e}"))?;
            self.handler_tasks.start_generation().map_err(|e| {
                anyhow::anyhow!("Failed to start WebSocket handler task generation: {e}")
            })?;
        }
        let handler_spawner = self.handler_tasks.spawner().map_err(|e| {
            anyhow::anyhow!("Failed to acquire WebSocket handler task spawner: {e}")
        })?;
        let handler_abort = CancellationToken::new();
        *self.handler_abort.lock() = handler_abort.clone();
        let mut rollback = ConnectRollback {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Call `disconnect()` (and await it) before calling `connect()` again.
  2. Reuse the existing connected client instead of reconnecting.
  3. Ensure `all_finished()` becomes true by letting prior tasks shut down (the client will auto-shutdown stale tasks only when not open); check `is_active` before connecting.

Example fix

// before
if !client.is_active() { client.connect().await?; } // stale handler may still run
// after
client.disconnect().await?;
client.connect().await?;
Defensive patterns

Strategy: try-catch

Validate before calling

// only connect when no handler tasks are pending
if client.is_active() || !client.is_disconnected() {
    // skip connect or disconnect first
}

Try / catch

match client.connect().await {
    Ok(()) => {},
    Err(e) if e.to_string().contains("previous WebSocket handler task") => {
        client.disconnect().await?;
        client.connect().await?;
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `connect()` on an already-connected client, or reconnecting while a prior connection's handler tasks are still running (no prior `disconnect()`).

Common situations: Reconnect logic that calls `connect` again after a transient drop without awaiting `disconnect` first; test code or retry loops re-invoking `connect` on a live client.

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/b4ca9fd93af35f02. Report an issue: GitHub.