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

  1. Read the joined messages to identify which teardown step(s) failed
  2. Ignore or log the error if the goal was merely to stop consuming (connection is gone anyway)
  3. 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

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


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