nautechsystems/nautilus_trader · error

{context}: {}

Error message

{context}: {}

What it means

Aggregated shutdown-failure error raised by the Lighter data client. When the client tears down (full shutdown, partial-connect teardown, or disconnect) it collects errors from joining each owned WebSocket/task; if any task failed to finish cleanly, take_shutdown_result joins them with '; ' and bails with the context prefix naming which teardown phase failed. The original per-task error text is embedded in the joined message.

Source

Thrown at crates/adapters/lighter/src/data/mod.rs:318

            &mut self.ws_disconnect_handle,
            "WebSocket disconnect",
            &mut self.shutdown_errors,
        )
        .await;

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

        self.take_shutdown_result("Failed to terminate Lighter tasks")
    }

    fn take_shutdown_result(&mut self, context: &str) -> anyhow::Result<()> {
        if self.shutdown_errors.is_empty() {
            Ok(())
        } else {
            let errors = std::mem::take(&mut self.shutdown_errors);
            anyhow::bail!("{context}: {}", errors.join("; "))
        }
    }

    async fn finish_owned_task(
        slot: &mut TaskSlot<Result<(), LighterWsError>>,
        description: &str,
        errors: &mut Vec<String>,
    ) {
        let Some(outcome) = finish_task(slot, DISCONNECT_TIMEOUT, DISCONNECT_TIMEOUT).await else {
            return;
        };

        match outcome {
            TaskJoinOutcome::Completed(Ok(())) | TaskJoinOutcome::Aborted => {}
            TaskJoinOutcome::Completed(Err(e)) => {
                errors.push(format!("{description} failed: {e}"));
            }
            TaskJoinOutcome::Failed(e) => {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the joined message after the context prefix to find the underlying per-task error and address it (usually a network/WS issue).
  2. Retry the disconnect/shutdown; transient transport errors during teardown are often non-fatal since the client is stopping anyway.
  3. Check network/proxy stability if teardown errors recur on every disconnect.
  4. If a specific task name appears in the joined errors, look at that task's logs for its root cause; report a bug if teardown consistently fails on a healthy connection.
Defensive patterns

Strategy: try-catch

Try / catch

match client.disconnect().await {
    Ok(()) => {}
    Err(e) if e.to_string().contains("Failed to terminate") => {
        tracing::warn!("teardown errors (client stopping anyway): {e:#}");
        // inspect joined per-task causes; retry if transient network issue
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling disconnect() or shutdown_tasks() on the Lighter data client, or a connect() that failed partway and triggers teardown_partial_connect, while one or more spawned WebSocket reader/writer/heartbeat tasks returned an error (e.g. closed transport, send on closed channel, task panicked).

Common situations: Network drop during disconnect causing WS task join errors; reconnect storms leaving stale tasks; broker/remote closing connections mid-teardown; shutting down while a task is mid-reconnect.

Related errors


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