nautechsystems/nautilus_trader · error · anyhow::Error
failed to start user stream dispatch task: {e}
Error message
failed to start user stream dispatch task: {e} What it means
Raised in BinanceFuturesExecutionClient::connect when spawning the tokio task that dispatches Binance user-data-stream messages (run_user_stream_dispatch) fails. The spawn itself returned an error (e.g. JoinHandle error from the runtime), so the client cannot consume account/order updates from the private WebSocket stream and connect aborts rather than running silently without user-stream data.
Source
Thrown at crates/adapters/binance/src/futures/execution.rs:1845
proxy_url: self.config.proxy_url.clone(),
socket_factory: self.socket_factory.clone(),
};
let ws_client =
build_and_connect_user_stream(&ws_build_params, listen_key.expose_secret()).await?;
let stream = ws_client.stream();
*self.ws_client.lock() = Some(ws_client);
self.ws_task
.lock()
.await
.spawn(run_user_stream_dispatch(
stream,
dispatch_ctx.clone(),
recovery_tx.clone(),
dispatch_user_stream_message,
))
.map_err(|e| anyhow::anyhow!("failed to start user stream dispatch task: {e}"))?;
// Start listen key keepalive task
{
let http_client = self.http_client.clone();
let listen_key_ref = self.listen_key.clone();
let cancel = self.cancellation_token.clone();
let recovery_tx = recovery_tx.clone();
self.session_tasks.spawn(async move {
let mut interval =
tokio::time::interval(Duration::from_secs(LISTEN_KEY_KEEPALIVE_SECS));
let mut consecutive_failures: u32 = 0;
loop {
tokio::select! {
_ = interval.tick() => {
let key = {
let guard = listen_key_ref.read();View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure the tokio runtime is running and not shutting down when connect() is called; await connect fully before beginning shutdown.
- Retry connect after a clean disconnect once the runtime is healthy.
- Check the inner `{e}` message for the concrete spawn failure (e.g. shutdown vs panic) and address that cause.
- If using a custom runtime setup, verify ws_task's handle belongs to the same live runtime as the client.
Example fix
// before
// begin_shutdown called concurrently with connect
tokio::join!(async { client.connect().await }, shutdown());
// after
client.connect().await?;
// then trigger shutdown
shutdown().await; Defensive patterns
Strategy: retry
Try / catch
match client.connect().await {
Err(e) if e.to_string().contains("dispatch task") => {
// runtime likely shutting down; retry on a live runtime
tokio::time::sleep(Duration::from_secs(1)).await;
client.connect().await?;
}
r => r?,
} Prevention
- Only call connect() from a live, healthy tokio runtime
- Never begin node shutdown concurrently with connect()
- Read the inner spawn error to distinguish shutdown from panic
When it happens
Trigger: self.ws_task spawn of run_user_stream_dispatch returns Err — typically when the tokio runtime is shutting down, the spawning task handle's runtime has been dropped, or resource exhaustion prevents new task creation during connect().
Common situations: Stopping a live node while connect() is mid-flight, running the node with a runtime that is being torn down, or calling connect from outside a live async runtime context.
Related errors
- Failed to start Spot public JSON WS bytes task: {e}
- Failed to start Spot public JSON WS handler task: {e}
- Failed to start Hyperliquid WebSocket handler task: {e}
- failed to start recovered user stream dispatch task: {e}
- failed to register Polymarket message handler: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/5cb94e6fb6d72e60.
Report an issue: GitHub.