nautechsystems/nautilus_trader · error · anyhow::Error

{shutdown_errors joined with "; "}

Error message

{shutdown_errors joined with "; "}

What it means

During teardown (partial-connect cleanup or disconnect), the client shuts down background tasks and collects any failures into shutdown_errors. If any teardown step failed, the accumulated messages are joined with "; " and returned as a single aggregated error.

Source

Thrown at crates/adapters/hyperliquid/src/execution.rs:634

        self.begin_session_shutdown();
        self.pending_tasks.begin_shutdown();

        if let Err(e) = self.ws_client.disconnect().await {
            self.shutdown_errors
                .push(format!("Hyperliquid WebSocket shutdown failed: {e}"));
        }

        if let Err(e) = self.await_session_tasks().await {
            self.shutdown_errors.push(e.to_string());
        }

        if let Err(e) = self.await_pending_tasks().await {
            self.shutdown_errors.push(e.to_string());
        }
        self.core.set_disconnected();

        if !self.shutdown_errors.is_empty() {
            anyhow::bail!(std::mem::take(&mut self.shutdown_errors).join("; "));
        }
        Ok(())
    }

    async fn await_pending_tasks(&self) -> anyhow::Result<()> {
        self.pending_tasks.begin_shutdown();
        self.pending_tasks
            .finish_shutdown(Duration::from_secs(1), Duration::from_secs(2))
            .await
            .map_err(|e| anyhow::anyhow!("Failed to terminate Hyperliquid execution tasks: {e}"))?;
        Ok(())
    }

    async fn await_session_tasks(&self) -> anyhow::Result<()> {
        self.session_tasks.begin_shutdown();
        self.session_tasks
            .finish_shutdown(Duration::from_secs(1), Duration::from_secs(2))
            .await

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the joined message to identify which specific shutdown step(s) failed and address those underlying errors first.
  2. Retry disconnect() — shutdown_errors is taken (cleared) on this error, so a fresh attempt starts clean.
  3. Ensure tasks are cancelled and data streams are closed before dropping the client (unsubscribe/cancel outstanding requests).
  4. Upgrade the adapter if the underlying failure looks like a teardown race/bug.
Defensive patterns

Strategy: try-catch

Try / catch

if let Err(e) = client.disconnect().await {
    log::error!("disconnect reported shutdown failures: {e}");
    // retry once; shutdown_errors was cleared by the failed attempt
    client.disconnect().await?;
}

Prevention

When it happens

Trigger: Calling disconnect (or failing a connect and running teardown_partial_connect) while one or more shutdown steps fail — e.g. await_pending_tasks reports a task error, WebSocket close fails, or command-sender shutdown fails; all messages are joined and raised together.

Common situations: Disconnecting while subscriptions/tasks are still in flight and erroring during cancellation; a partially initialized client being torn down after a failed connect; network errors during WebSocket close.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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