nautechsystems/nautilus_trader · error

Failed to start Betfair session generation: {e}

Error message

Failed to start Betfair session generation: {e}

What it means

connect (execution client) starts a new task generation for session_tasks. If start_generation fails (e.g. the group is still shutting down or the generation is already open), teardown_partial_connect is invoked first and the error is wrapped with this message.

Source

Thrown at crates/adapters/betfair/src/execution.rs:1448

            client.begin_shutdown();
        }

        self.clear_resync_state();
        log::info!("Stopped: client_id={}", self.core.client_id);
        Ok(())
    }

    async fn connect(&mut self) -> anyhow::Result<()> {
        if self.core.is_connected() && self.session_tasks.is_open() && self.pending_tasks.is_open()
        {
            return Ok(());
        }

        if !self.session_tasks.is_open() || !self.pending_tasks.is_open() {
            self.teardown_partial_connect().await?;
            self.session_tasks
                .start_generation()
                .map_err(|e| anyhow::anyhow!("Failed to start Betfair session generation: {e}"))?;
            self.pending_tasks
                .start_generation()
                .map_err(|e| anyhow::anyhow!("Failed to start Betfair task generation: {e}"))?;
        }

        let http_cancellation = self.http_client.cancellation_token();
        let setup_guard =
            TaskGroupGuard::new(&[&self.session_tasks, &self.pending_tasks], move || {
                http_cancellation.cancel();
            });

        register_betfair_custom_data();

        let session_token_result = async {
            self.http_client
                .connect()
                .await
                .map_err(|e| anyhow::anyhow!("{e}"))?;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure connect/disconnect calls are strictly sequential
  2. Wait for teardown_partial_connect of the previous attempt to finish before reconnecting
  3. Inspect the inner error `e` from start_generation for the registry state

Example fix

// before: overlapping connects
tokio::join!(client.connect(), client.connect());
// after
client.connect().await?;
// later
client.disconnect().await?;
client.connect().await?;
Defensive patterns

Strategy: validation

Validate before calling

if client.is_connected() { return Ok(()); } // avoid double connect

Type guard

fn can_connect(client: &BetfairExecClient) -> bool {
    !client.is_connected() && !client.is_shutting_down()
}

Try / catch

if let Err(e) = client.connect().await {
    if e.to_string().contains("session generation") {
        tokio::time::sleep(Duration::from_secs(1)).await;
        client.connect().await?;
    } else { return Err(e); }
}

Prevention

When it happens

Trigger: session_tasks or pending_tasks group is not open (mid-shutdown or never initialized) and start_generation still fails — concurrent connect calls, or a prior connect's teardown not yet finished.

Common situations: Calling connect twice without disconnect; reconnect racing a prior shutdown; a previous failed connect leaving the groups in a half-open state.

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/f5461178f8c9365c. Report an issue: GitHub.