nautechsystems/nautilus_trader · error

Failed to start Betfair data session tasks: {e}

Error message

Failed to start Betfair data session tasks: {e}

What it means

This error wraps a failure from session_tasks.start_generation() in prepare_task_groups, which reopens the session TaskGroup before (re)connecting the Betfair data client. Task groups are one-shot per generation: after a shutdown they must be restarted to spawn new tasks. If start_generation returns Err, the adapter cannot begin a new connect lifecycle.

Source

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

        let (session_result, command_result) = tokio::join!(
            self.session_tasks
                .finish_shutdown(Duration::from_secs(1), Duration::from_secs(2)),
            self.command_tasks
                .finish_shutdown(Duration::from_secs(1), Duration::from_secs(2)),
        );
        session_result
            .map_err(|e| anyhow::anyhow!("Failed to finish Betfair data session tasks: {e}"))?;
        command_result
            .map_err(|e| anyhow::anyhow!("Failed to finish Betfair data command tasks: {e}"))?;
        Ok(())
    }

    async fn prepare_task_groups(&mut self) -> anyhow::Result<()> {
        if !self.session_tasks.is_open() || !self.command_tasks.is_open() {
            self.teardown_partial_connect().await?;
            self.session_tasks
                .start_generation()
                .map_err(|e| anyhow::anyhow!("Failed to start Betfair data session tasks: {e}"))?;
            self.command_tasks
                .start_generation()
                .map_err(|e| anyhow::anyhow!("Failed to start Betfair data command tasks: {e}"))?;
        }
        Ok(())
    }

    fn begin_stream_shutdown(&self) {
        for stream in self.stream_shutdowns.lock().iter() {
            stream.begin_shutdown();
        }
    }

    async fn teardown_partial_connect(&mut self) -> anyhow::Result<()> {
        self.session_tasks.begin_shutdown();
        self.command_tasks.begin_shutdown();
        self.begin_stream_shutdown();
        self.is_connected.store(false, Ordering::Relaxed);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure teardown_partial_connect fully completes (including finish_tasks) before attempting connect again
  2. Check the inner `{e}` from start_generation to determine the offending TaskGroup state
  3. Avoid concurrent or overlapping connect() calls on the same BetfairDataClient instance
  4. Recreate the data client if the task group cannot be restarted after repeated failures

Example fix

// before
self.session_tasks.start_generation().map_err(|e| anyhow::anyhow!("Failed to start Betfair data session tasks: {e}"))?;
// after
if !self.session_tasks.is_open() {
    self.session_tasks.start_generation().map_err(|e| anyhow::anyhow!("Failed to start Betfair data session tasks: {e}"))?;
}
Defensive patterns

Strategy: retry

Validate before calling

// before reconnecting, ensure the client is fully disconnected
assert!(!client.is_connected(), "previous connect still active");

Try / catch

match client.connect().await {
    Err(e) if e.to_string().contains("Failed to start Betfair data session tasks") => {
        tokio::time::sleep(Duration::from_secs(2)).await;
        client.disconnect().await.ok(); // force clean teardown, then retry
    }
    Err(e) => return Err(e),
    Ok(()) => {},
}

Prevention

When it happens

Trigger: connect() -> prepare_task_groups sees session_tasks.is_open() == false (group was shut down by a previous disconnect or failed connect), calls teardown_partial_connect, then start_generation() fails. Typically when the group state does not permit restarting a generation (already running, or shutdown not fully finalized).

Common situations: Rapid reconnect sequences where teardown did not fully complete before the next connect; concurrent connect calls on the same adapter; a prior finish_shutdown failure leaving the group in an inconsistent 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/8a6661ca49955250. Report an issue: GitHub.