nautechsystems/nautilus_trader · error · anyhow::Error

Failed to register blockchain process task: {e}

Error message

Failed to register blockchain process task: {e}

What it means

This error is thrown when the tokio runtime refuses to spawn the background task that processes live blockchain messages after a WebSocket connect. `session_tasks.spawn(future)` returns an `JoinError`/spawn failure (most commonly the tokio runtime being shut down, which panics inside `tokio::spawn` semantics surfaced through the task set, or the task set being closed). The client wraps that failure in anyhow with this message and aborts the connect.

Source

Thrown at crates/adapters/blockchain/src/data/client.rs:249

                                    &mut core_client,
                                    &mut pending_pool_messages,
                                )
                                .await;
                            }
                            Err(e) => {
                                log::error!("Error processing RPC message: {e}");
                            }
                        }
                    }
                }
            }

            log::debug!("Stopped task 'process'");
        };

        self.session_tasks
            .spawn(future)
            .map_err(|e| anyhow::anyhow!("Failed to register blockchain process task: {e}"))?;
        Ok(startup_rx)
    }

    async fn process_live_blockchain_message(
        msg: BlockchainMessage,
        core_client: &mut BlockchainDataClientCore,
        pending_pool_messages: &mut VecDeque<BlockchainMessage>,
    ) {
        let is_block = matches!(&msg, BlockchainMessage::Block(_));
        let Some(msg) =
            Self::ready_live_blockchain_message(msg, &core_client.cache, pending_pool_messages)
        else {
            return;
        };

        if let Some(data) = Self::data_event_from_blockchain_message(msg, core_client).await {
            core_client.send_data(data);
        }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure connect()/spawn_process_task runs inside a live multi-threaded tokio runtime and that the runtime is not being dropped or shut down during connect.
  2. Check whether a prior task in `session_tasks` panicked or was aborted, closing the task set; fix the underlying panic or re-create the client instead of reusing it after shutdown.
  3. If the client reconnects during teardown, guard the reconnect so it is cancelled when the runtime is shutting down (e.g. check a shutdown token before connect).
  4. Log the inner JoinError (`{e}`) for details — it names whether the spawn failed because the runtime was gone or the task was cancelled.

Example fix

// before: runtime shutting down mid-connect
client.connect().await?;
// after: ensure shutdown completes before reconnecting
if shutdown_token.is_cancelled() { return Ok(()); }
runtime_handle.clone().spawn(async move { client.connect().await })
    .await
    .map_err(|e| anyhow::anyhow!("Failed to register blockchain process task: {e}"))??;
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust: verify we are inside a live runtime before connecting
assert!(tokio::runtime::Handle::try_current().is_ok(), "connect() requires a live tokio runtime");

Try / catch

match client.connect().await {
    Ok(rx) => { /* use startup_rx */ }
    Err(e) if e.to_string().contains("Failed to register blockchain process task") => {
        log::warn!("Runtime shutting down; skipping blockchain client connect: {e}");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `connect()` on BlockchainDataClient when the enclosing tokio runtime is being shut down, when the `session_tasks` TaskSet/JoinSet has already been closed or aborted, or when the runtime lacks a reactor/worker context (e.g. spawn attempted outside a multi-threaded runtime with blocking workers).

Common situations: Applications shutting down their tokio runtime while the data client still tries to (re)connect; calling connect() from a blocking context or a runtime that is being dropped; a panic in the spawned future causing the task set to be closed; embedding the client in a runtime with `enable_time` only (no I/O driver).

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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