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
- 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.
- 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.
- 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).
- 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
- Keep the tokio runtime alive for the entire lifetime of the client; shut down the client before dropping the runtime.
- Do not call connect() from blocking contexts or runtimes without an I/O driver.
- Fix panics in background tasks promptly — a panicked task can poison the session task set.
- Gate reconnection logic on a shutdown token so teardown does not race with connect().
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
- Failed to register Betfair keep-alive task: {e}
- Failed to register Betfair reconnect task: {e}
- failed to register Polymarket instrument refresh: {e}
- failed to register Polymarket message handler: {e}
- failed to register Polymarket resolve poll: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/62c0498c28cea950.
Report an issue: GitHub.