nautechsystems/nautilus_trader · warning
errors.join("; ") (aggregated shutdown errors)
Error message
errors.join("; ") (aggregated shutdown errors) What it means
disconnect() collects any errors encountered while shutting down the WebSocket (handler join failures, command send failures) into shutdown_errors and, if non-empty, fails with all of them joined by "; ". It signals the shutdown path partially failed even though the disconnect was requested.
Source
Thrown at crates/adapters/coinbase/src/websocket/client.rs:538
if tokio::time::Instant::now() >= deadline {
self.shutdown_errors
.push("Timed out waiting for WebSocket to reach Closed state".to_string());
break;
}
tokio::time::sleep(Duration::from_millis(20)).await;
}
if let Some(control) = &self.socket_control {
control.deregister();
}
if self.shutdown_errors.is_empty() {
Ok(())
} else {
let errors = std::mem::take(&mut self.shutdown_errors);
anyhow::bail!(errors.join("; "))
}
}
/// Returns true if the WebSocket connection is active.
#[must_use]
pub fn is_active(&self) -> bool {
let mode_ptr = self.connection_mode.load();
let mode_val = mode_ptr.load(Ordering::Relaxed);
ConnectionMode::from_u8(mode_val).is_active()
}
/// Returns true if the WebSocket is reconnecting after a transport drop.
#[must_use]
pub fn is_reconnecting(&self) -> bool {
let mode_ptr = self.connection_mode.load();
let mode_val = mode_ptr.load(Ordering::Relaxed);
ConnectionMode::from_u8(mode_val).is_reconnect()
}View on GitHub (pinned to 18893faf8b)
Solutions
- Read the joined messages to identify which teardown step(s) failed
- Ignore or log the error if the goal was merely to stop consuming (connection is gone anyway)
- Ensure connect() succeeded and is_active() before disconnecting to avoid teardown on a dead connection
Example fix
// before
ws.disconnect().await?;
// after
if ws.is_active() {
if let Err(e) = ws.disconnect().await { log::warn!("shutdown errors: {e}"); }
} Defensive patterns
Strategy: try-catch
Validate before calling
if !ws.is_active() { return Ok(()); } // nothing to disconnect Try / catch
if let Err(e) = ws.disconnect().await {
// best-effort shutdown: log joined errors instead of failing the app
log::warn!("Coinbase ws shutdown issues: {e}");
} Prevention
- Treat shutdown errors as best-effort warnings in application teardown paths
- Check is_active() before disconnecting
- Avoid disconnecting twice; track connection state
When it happens
Trigger: Calling disconnect() after the handler already errored/died, or when aborting the task or draining the output channel fails; the accumulated errors are reported together.
Common situations: App shutdown racing an already-broken WebSocket; repeated disconnect() calls; underlying network failure during the final unsubscribe/teardown.
Related errors
- failed to finish Binance Futures data command tasks: {e}
- Binance Futures data teardown failed: {}
- Coinbase WebSocket handler failed: {error}
- Coinbase WebSocket handler did not stop after abort
- Failed to send SetClient command: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/06bba1e5b21bff66.
Report an issue: GitHub.