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
- Read the joined message after the context prefix to find the underlying per-task error and address it (usually a network/WS issue).
- Retry the disconnect/shutdown; transient transport errors during teardown are often non-fatal since the client is stopping anyway.
- Check network/proxy stability if teardown errors recur on every disconnect.
- 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
- Disconnect on a stable connection rather than mid-reconnect when possible.
- Treat teardown errors as advisory when the process is exiting anyway; log the joined causes.
- Monitor network stability if this recurs.
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
- Failed to terminate Lighter tasks: {}
- std::mem::take(&mut self.shutdown_errors).join("; ")
- noid '{}' does not match new order oid '{}'
- failed to finish Binance Futures data command tasks: {e}
- Binance Futures data teardown failed: {}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/a572cd02e2592566.
Report an issue: GitHub.