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

  1. Ensure the tokio runtime is running and not shutting down when connect() is called; await connect fully before beginning shutdown.
  2. Retry connect after a clean disconnect once the runtime is healthy.
  3. Check the inner `{e}` message for the concrete spawn failure (e.g. shutdown vs panic) and address that cause.
  4. 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

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


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