nautechsystems/nautilus_trader · error

errors joined with "; " (aggregated disconnect errors)

Error message

errors joined with "; " (aggregated disconnect errors)

What it means

During disconnect or a failed partial connect, the Deribit adapter tears down session and pending tokio tasks and collects any error each teardown step produced. Instead of failing on the first error, it aggregates all of them and returns a single anyhow error with the messages joined by "; ". This means the connection teardown hit one or more problems (e.g. tasks failed to shut down cleanly, channel sends failed), all of which are reported at once.

Source

Thrown at crates/adapters/deribit/src/data.rs:263

        }

        let mut errors = Vec::new();

        if let Some(ws) = self.ws_client.as_ref()
            && let Err(e) = ws.close().await
        {
            errors.push(format!("WebSocket shutdown failed: {e}"));
        }

        if let Err(e) = self.finish_tasks().await {
            errors.push(e.to_string());
        }
        self.is_connected.store(false, Ordering::Release);

        if errors.is_empty() {
            Ok(())
        } else {
            anyhow::bail!(errors.join("; "))
        }
    }

    /// Gets the interval from params, defaulting to Raw if authenticated.
    ///
    /// If authenticated, we prefer Raw interval for best data quality.
    /// Users can still override via params if they want 100ms or agg2.
    fn get_interval(&self, params: &Option<Params>) -> Option<DeribitUpdateInterval> {
        if let Some(interval) = params
            .as_ref()
            .and_then(|p| p.get_str("interval"))
            .and_then(|s| s.parse::<DeribitUpdateInterval>().ok())
        {
            return Some(interval);
        }

        // Default to Raw if authenticated, otherwise None (100ms default)
        if let Some(ws) = self.ws_client.as_ref()

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the joined messages to identify which sub-step(s) failed; each segment names one underlying cause
  2. Check network/WS connectivity to Deribit and retry the disconnect once tasks have settled
  3. Ensure you do not double-disconnect or drop the client while requests are in flight
  4. If a task hangs, inspect session_tasks/pending_tasks shutdown paths (TaskGroupGuard) for blocking joins and consider a shutdown timeout

Example fix

// before: blindly retrying connect after failed teardown
client.connect().await?;
// after: surface and log each aggregated cause before reconnecting
if let Err(e) = client.disconnect().await {
    for cause in e.to_string().split("; ") {
        tracing::warn!(cause, "teardown error");
    }
}
client.connect().await?;
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure clean state before teardown
assert!(!client.is_connected(), "already disconnected");

Try / catch

match client.disconnect().await {
    Err(e) => e.to_string().split("; ").for_each(|c| log::warn!("teardown: {c}")),
    Ok(()) => {},
}

Prevention

When it happens

Trigger: Calling client.disconnect(), or connect() when the WebSocket/HTTP setup partially fails, causes teardown_partial_connect to run; if any spawned task join or shutdown step errors, the errors vec is non-empty and bail!(errors.join("; ")) fires.

Common situations: Network drop mid-session so the WS task already died and shutdown signals fail; calling disconnect twice or disconnecting while tasks are stuck waiting on the remote; cancel-safe shutdown of pending order tasks that never received a response.

Related errors


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