nautechsystems/nautilus_trader · error

Failed to disconnect data clients: {}

Error message

Failed to disconnect data clients: {}

What it means

The engine's disconnect method sends disconnect commands to all registered data clients and collects per-client errors. If any client failed to disconnect, it aggregates all error messages (joined with "; ") into a single error. This is a wrapper error — the real causes are in the inner per-client messages.

Source

Thrown at crates/data/src/engine/mod.rs:776

    ///
    /// # Errors
    ///
    /// Returns an error if any client fails to disconnect.
    pub async fn disconnect(&mut self) -> anyhow::Result<()> {
        let futures: Vec<_> = self
            .get_clients_mut()
            .into_iter()
            .map(DataClientAdapter::disconnect)
            .collect();

        let results = join_all(futures).await;
        let errors: Vec<_> = results.into_iter().filter_map(Result::err).collect();

        if errors.is_empty() {
            Ok(())
        } else {
            let error_msgs: Vec<_> = errors.iter().map(ToString::to_string).collect();
            anyhow::bail!(
                "Failed to disconnect data clients: {}",
                error_msgs.join("; ")
            )
        }
    }

    /// Returns `true` if all registered data clients are currently connected.
    #[must_use]
    pub fn check_connected(&self) -> bool {
        self.get_clients()
            .iter()
            .all(|client| client.is_connected())
    }

    /// Returns `true` if all registered data clients are currently disconnected.
    #[must_use]
    pub fn check_disconnected(&self) -> bool {
        self.get_clients()

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Parse the joined message after "Failed to disconnect data clients: " and split on "; " to identify which clients failed.
  2. Fix the root cause per client (check network connectivity, adapter logs, and that clients were connected before disconnect).
  3. Make disconnect idempotent/tolerant in your shutdown path: log the aggregate error and continue teardown rather than aborting.

Example fix

// before
engine.disconnect()?; // aborts whole shutdown on one bad client
// after
if let Err(e) = engine.disconnect() {
    log::warn!("data client disconnect issues: {e}");
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust
// check client connectivity before disconnecting
let dead: Vec<_> = client_ids.iter().filter(|id| !engine.is_client_connected(id)).collect();
if !dead.is_empty() { log::warn!("clients already disconnected: {dead:?}"); }

Try / catch

// Rust
match engine.disconnect() {
    Ok(()) => {},
    Err(e) => {
        let inner = e.to_string();
        for msg in inner.strip_prefix("Failed to disconnect data clients: ").unwrap_or(&inner).split("; ") {
            log::warn!("disconnect issue: {msg}");
        }
    }
}

Prevention

When it happens

Trigger: Calling DataEngine::disconnect() when one or more underlying data clients return Err from their disconnect (network failures, adapter-side disconnect errors, clients already in a bad state).

Common situations: Shutting down a node where a websocket/adapter connection is already broken; adapter disconnect RPC timing out; multiple adapter failures during teardown causing a long joined error string.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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