nautechsystems/nautilus_trader · error
Failed to disconnect data clients: {data_err}; failed to dis
Error message
Failed to disconnect data clients: {data_err}; failed to disconnect execution clients: {exec_err} What it means
Kernel::disconnect_clients shuts down data and execution client connections concurrently via futures::join!. If both disconnect futures fail, neither error is preferred, so both are combined into one message identifying each side. A single failure is returned as-is; only the both-failed case produces this combined error.
Source
Thrown at crates/system/src/kernel.rs:1095
/// Disconnects all engine clients.
///
/// # Errors
///
/// Returns an error if any client fails to disconnect.
#[expect(clippy::await_holding_refcell_ref)] // Single-threaded runtime, intentional design
pub async fn disconnect_clients(&mut self) -> anyhow::Result<()> {
log::info!("Disconnecting clients...");
let mut data_engine = self.data_engine.borrow_mut();
let mut exec_engine = self.exec_engine.borrow_mut();
let (data_result, exec_result) =
futures::join!(data_engine.disconnect(), exec_engine.disconnect());
match (data_result, exec_result) {
(Ok(()), Ok(())) => Ok(()),
(Err(data_err), Ok(())) => Err(data_err),
(Ok(()), Err(exec_err)) => Err(exec_err),
(Err(data_err), Err(exec_err)) => anyhow::bail!(
"Failed to disconnect data clients: {data_err}; failed to disconnect execution \
clients: {exec_err}"
),
}
}
/// Returns `true` if all engine clients are connected.
#[must_use]
pub fn check_engines_connected(&self) -> bool {
self.data_engine.borrow().check_connected() && self.exec_engine.borrow().check_connected()
}
/// Returns `true` if all engine clients are disconnected.
#[must_use]
pub fn check_engines_disconnected(&self) -> bool {
self.data_engine.borrow().check_disconnected()
&& self.exec_engine.borrow().check_disconnected()
}View on GitHub (pinned to 18893faf8b)
Solutions
- Inspect both halves of the message; fix the underlying transport/venue issue for each side separately
- If clients are already dead, this may be benign — verify no orders/state were left dirty and continue shutdown
- Add retry or timeout handling around disconnect if the venue is intermittently unreachable
- Check client logs for whether disconnect errors indicate already-closed connections (often ignorable at teardown)
Defensive patterns
Strategy: retry
Validate before calling
// Probe connectivity before shutdown-sensitive operations
let data_ok = data_engine.ping().is_ok();
let exec_ok = exec_engine.ping().is_ok();
log::info!("disconnect pre-check: data_ok={data_ok} exec_ok={exec_ok}"); Try / catch
match kernel.disconnect_clients().await {
Err(e) if e.to_string().contains("Failed to disconnect data clients") => {
// both sides failed; treat as best-effort teardown, log both halves
log::warn!("disconnect: {e}");
}
other => other?,
} Prevention
- Treat disconnect failures of dead connections as best-effort during teardown
- Implement bounded retries with backoff in client disconnect paths
- Monitor venue connectivity so failures at shutdown are anticipated
When it happens
Trigger: Calling kernel.disconnect_clients() (or node stop/shutdown) when both the data engine's and the execution engine's disconnect() fail — e.g. both venue WebSocket sessions are already dead or their clients error during teardown.
Common situations: Network outage at shutdown so both connections abort with transport errors; venue-side session termination; client cleanup code raising errors for both engines simultaneously.
Related errors
- Binance Futures data teardown failed: {}
- rate limiter decision lock poisoned
- Close command should not be drained
- Invalid `ConnectionMode` value: {value}
- Betfair data shutdown failed: {}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/b37bcab140fd7bdb.
Report an issue: GitHub.