nautechsystems/nautilus_trader · error

Failed to start Betfair task generation: {e}

Error message

Failed to start Betfair task generation: {e}

What it means

Immediately after starting the session generation, connect starts the pending_tasks generation for order/account requests. If start_generation fails, teardown_partial_connect runs and the error is wrapped with this message.

Source

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

        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}"))?;

            let funds: AccountFundsResponse = self
                .http_client

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Serialize connect/disconnect calls
  2. Await completion of any previous teardown before reconnecting
  3. Log the inner `e` to identify whether the group was closed or already open

Example fix

// before: racing reconnect with shutdown
disconnect_future.abort();
client.connect().await?;
// after: await teardown first
disconnect_future.await.ok();
client.connect().await?;
Defensive patterns

Strategy: validation

Validate before calling

if client.is_connected() { return Ok(()); }

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("task generation") {
        client.disconnect().await.ok();
        client.connect().await?;
    } else { return Err(e); }
}

Prevention

When it happens

Trigger: pending_tasks group not in a startable state — concurrent connects, prior shutdown still in progress, or session generation started but pending generation refused.

Common situations: Same lifecycle races as session generation: double connect, reconnect during teardown, half-initialized client after a failed connect.

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