nautechsystems/nautilus_trader · error · anyhow::Error
Binance Futures dispatch task did not stop after abort
Error message
Binance Futures dispatch task did not stop after abort
What it means
await_dispatch_task aborted the dispatch task and waited on its join handle; a TaskJoinOutcome::Incomplete means the task neither completed nor acknowledged the abort within the allowed window. This indicates a wedged dispatch loop that ignored cancellation during connect/disconnect.
Source
Thrown at crates/adapters/binance/src/futures/execution.rs:947
async fn await_dispatch_task(&self) -> anyhow::Result<()> {
let _recovery_guard = self.recovery_lock.lock().await;
let mut task_slot = self.ws_task.lock().await;
let Some(outcome) = finish_task(
&mut task_slot,
Duration::from_secs(1),
Duration::from_secs(2),
)
.await
else {
return Ok(());
};
match outcome {
TaskJoinOutcome::Completed(()) | TaskJoinOutcome::Aborted => Ok(()),
TaskJoinOutcome::Failed(e) => {
Err(anyhow::anyhow!("Binance Futures dispatch task failed: {e}"))
}
TaskJoinOutcome::Incomplete => Err(anyhow::anyhow!(
"Binance Futures dispatch task did not stop after abort"
)),
}
}
async fn close_listen_key_slot(
&self,
slot: &RwLock<Option<SecretString>>,
context: &str,
) -> anyhow::Result<()> {
let key = slot.read().clone();
let Some(key) = key else {
return Ok(());
};
self.http_client
.close_listen_key(key.expose_secret())
.awaitView on GitHub (pinned to 18893faf8b)
Solutions
- Retry the disconnect after the runtime settles
- Check for blocking calls or long-held locks in any custom handlers feeding the dispatch loop
- Ensure the tokio runtime has adequate worker threads and is not starved
- Upgrade to the latest nautilus_trader version; report a bug if it reproduces, as this indicates a cancellation-safety issue
Example fix
// before
client.disconnect().await?; // may hang/fail if dispatch task is wedged
// after
if let Err(e) = client.disconnect().await {
tracing::warn!("disconnect incomplete: {e:#}");
tokio::time::sleep(Duration::from_secs(1)).await;
client.disconnect().await?;
} Defensive patterns
Strategy: retry
Try / catch
if let Err(e) = client.disconnect().await {
if e.to_string().contains("did not stop after abort") {
tokio::time::sleep(Duration::from_secs(1)).await;
client.disconnect().await?;
} else { return Err(e); }
} Prevention
- Avoid blocking calls inside async message handlers
- Size the tokio runtime adequately to avoid task starvation
- Report reproducible cases; this points to a cancellation-safety bug
When it happens
Trigger: Calling connect or disconnect when the dispatch task is blocked in a non-cancellation-safe operation (e.g. a lock held across await, a blocking call in the async loop) and does not finish after abort is requested.
Common situations: Deadlocks in runtime under heavy load, blocking I/O inside the async dispatch loop, or tokio runtime starvation preventing the aborted task from being polled to completion.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
Related errors
- Failed to terminate Binance Futures session tasks: {e}
- Binance Futures dispatch task failed: {e}
- Invalid config type for BinanceExecutionClientFactory. Expec
- Instrument not found in cache: {symbol}
- Unsupported `OrderSide` for Binance: {value:?}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/4448617065037d7a.
Report an issue: GitHub.