nautechsystems/nautilus_trader · error

Failed to terminate Lighter tasks

Error message

Failed to terminate Lighter tasks: {}

What it means

Aggregated shutdown-failure error from the Lighter execution client. finish_session_shutdown (invoked by connect on failure and by disconnect) drains shutdown_errors accumulated from finishing owned tasks (WS senders, writers, nonce manager, etc.); if any are present it bails with this message and the joined per-task errors. It signals the execution session's background tasks did not all terminate cleanly.

Solutions

  1. Inspect the joined error text after the prefix to identify which task failed and why.
  2. Retry disconnect(); teardown errors during an outage typically resolve once connectivity is restored and usually don't affect already-submitted orders.
  3. Check connectivity/proxy stability if this fires on every shutdown.
  4. If shutdown consistently reports errors on a healthy connection, capture the full message and file a bug with the task description listed in it.
Defensive patterns

Strategy: try-catch

Try / catch

match exec_client.disconnect().await {
    Ok(()) => {}
    Err(e) if e.to_string().starts_with("Failed to terminate Lighter tasks") => {
        tracing::warn!("execution teardown errors: {e:#}");
        // parse joined task errors; retry disconnect if network was down
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling disconnect() on the Lighter execution client (or connect() aborting partway) while one or more owned tasks returned an error on completion - e.g. WS send channel already closed, writer task failed on a broken transport, or a task panicked.

Common situations: Disconnecting while an order submission is in flight and the writer task errors; network outage during teardown; repeated reconnect cycles leaving errored task handles; nonce manager task failing during shutdown.

Related errors


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

Appendix: source

Thrown at crates/adapters/lighter/src/execution.rs:479

        )
        .await;

        if let Err(e) = self
            .pending_tasks
            .finish_shutdown(Duration::from_secs(1), DISCONNECT_TIMEOUT)
            .await
        {
            self.shutdown_errors
                .push(format!("client-owned tasks failed: {e}"));
        }

        let stale = self.dispatch.take_pending_sendtx();
        warn_pending_sendtx_unknown(&stale, "session shutdown");
        self.nonce_recovery_inflight.store(false, Ordering::Release);

        if !self.shutdown_errors.is_empty() {
            let errors = std::mem::take(&mut self.shutdown_errors);
            anyhow::bail!("Failed to terminate Lighter tasks: {}", errors.join("; "));
        }
        Ok(())
    }

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

        match outcome {
            TaskJoinOutcome::Completed(()) => {
                log::debug!("Lighter {description} task completed");
            }
            TaskJoinOutcome::Aborted => {

View on GitHub (pinned to 18893faf8b)