nautechsystems/nautilus_trader · error

Failed to register Betfair keep-alive task

Error message

Failed to register Betfair keep-alive task: {e}

What it means

During BetfairMarketDataClient::connect, a background keep-alive task is spawned to periodically re-send the keepAlive message and prevent the Betfair session from expiring. If the task registration itself fails (e.g. the runtime/task registry rejects the spawn), connect returns this anyhow error and the whole connect sequence fails.

Solutions

  1. Retry connect when the runtime is fully running, not during shutdown
  2. Ensure connect is called on a live multi-threaded tokio runtime and never concurrently with disconnect/teardown
  3. Inspect the inner error `e` in the message for the root registration failure

Example fix

// before: connect called during app shutdown
market_client.connect().await?;
// after: connect only while the runtime is healthy
assert!(!shutting_down.load(Ordering::SeqCst));
market_client.connect().await?;
Defensive patterns

Strategy: retry

When it happens

Trigger: The keep-alive task registration future returns Err while connect is running — e.g. the tokio runtime is shutting down, the registration channel is closed, or the underlying task-generation registry refuses a new task after shutdown was begun.

Common situations: Connecting during adapter teardown or executor shutdown; nested runtimes or blocking a runtime thread so the registration future cannot complete; calling connect concurrently with disconnect.

Related errors


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

Appendix: source

Thrown at crates/adapters/betfair/src/data.rs:1031

                            }
                        };

                        let _ = keep_alive_client
                            .with_session_token(|token| {
                                refresh_stream_sessions(
                                    keep_alive_stream.as_ref(),
                                    keep_alive_race_stream.as_deref(),
                                    keep_alive_cricket_stream.as_deref(),
                                    &keep_alive_app_key,
                                    token,
                                    session_replaced,
                                );
                            })
                            .await;
                        log::debug!("Betfair session keep-alive sent");
                    }
                })
                .map_err(|e| anyhow::anyhow!("Failed to register Betfair keep-alive task: {e}"))?;

            let reconnect_http = Arc::clone(&self.http_client);
            let reconnect_stream = Arc::clone(self.stream_client.as_ref().unwrap());
            let reconnect_race_stream = self.race_stream_client.as_ref().map(Arc::clone);
            let reconnect_cricket_stream = self.cricket_stream_client.as_ref().map(Arc::clone);
            let reconnect_app_key = self.credential.app_key().to_string();

            self.session_tasks
                .spawn(async move {
                    while reconnect_rx.recv().await.is_some() {
                        log::info!("Handling data stream reconnection");

                        let session_replaced = match reconnect_http.keep_alive_with_token().await {
                            Ok(_) => false,
                            Err(ref e) if e.is_login_failed() => {
                                log::warn!(
                                    "Session expired on reconnect, attempting re-login: {e}"
                                );

View on GitHub (pinned to 18893faf8b)