nautechsystems/nautilus_trader · error

{errors.join("; ")}

Error message

{errors.join("; ")}

What it means

close_connections tears down all slots in the public JSON stream pool and collects per-slot disconnect errors; if any slot failed to shut down cleanly it joins them with ';' and bails. The pool did disconnect its tasks, but at least one slot's handler/bytes task ended with an error during shutdown. This is an aggregated multi-error report.

Solutions

  1. Inspect the joined error list to identify which slot(s) failed and whether the failure is transient (network) or persistent.
  2. If transient, recreate/reconnect the client — connect() calls close_connections, so a fresh connect replaces the pool.
  3. Ensure close is not called concurrently with subscribe/unsubscribe on the same pool.
  4. If a slot task keeps failing, check stream names and connection stability; an already-dead task's error may be unavoidable at close time.
Defensive patterns

Strategy: try-catch

Try / catch

if let Err(e) = client.close().await {
    log::warn!("disconnect reported errors (may be benign during shutdown): {e:#}");
}

Prevention

When it happens

Trigger: Calling close_connections (via connect to re-connect, or explicit close) when one or more slot handler_task or bytes_task join results contain errors — e.g. a task already crashed with a WS error, or shutdown signals failed to be acknowledged.

Common situations: Reconnecting after a network drop where some slot tasks already died with errors; closing the client while Binance is unreachable; tearing down during process shutdown where tasks fail cancellation.

Related errors


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

Appendix: source

Thrown at crates/adapters/binance/src/spot/websocket/public_json/client.rs:291

            {
                batch.slots.remove(index);
            }
        }

        *self.out_tx.lock() = None;
        *self.out_rx.lock() = None;

        let errors = batch
            .slots
            .iter_mut()
            .flat_map(|slot| std::mem::take(&mut slot.shutdown_errors))
            .collect::<Vec<_>>();
        batch
            .slots
            .retain(|slot| slot.handler_task.is_some() || slot.bytes_task.is_some());

        if !errors.is_empty() {
            anyhow::bail!(errors.join("; "));
        }
        log::debug!("Disconnected from Binance Spot public JSON stream pool");
        Ok(())
    }

    /// Subscribes to stream names.
    ///
    /// # Errors
    ///
    /// Returns an error if command delivery fails or if the connection pool is exhausted.
    pub async fn subscribe(&self, streams: Vec<String>) -> anyhow::Result<()> {
        let _connect_guard = self.connect_lock.lock().await;

        // Phase 1: filter already-subscribed streams (brief lock)
        let new_streams: Vec<String> = {
            let slots = self.slots.lock();

            if self.signal.load(Ordering::Acquire) {

View on GitHub (pinned to 18893faf8b)