nautechsystems/nautilus_trader · error

Failed to finish Betfair data command tasks: {e}

Error message

Failed to finish Betfair data command tasks: {e}

What it means

This error wraps a failure from TaskGroup::finish_shutdown when the Betfair data client tears down its session or command task groups during teardown_partial_connect/finish_tasks. finish_shutdown gives each group a drain window (1s / 2s timeouts); if tasks do not complete or abort cleanly within it, the group returns an error. It means background tokio tasks (session/command handlers) could not be joined or aborted during disconnect.

Source

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

    where
        F: std::future::Future<Output = ()> + Send + 'static,
    {
        if let Err(e) = self.command_tasks.spawn(future) {
            log::warn!("Skipping Betfair data command after shutdown began: {e}");
        }
    }

    async fn finish_tasks(&self) -> anyhow::Result<()> {
        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() {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect Betfair session/command task bodies for blocking or long-lived awaits that ignore cancellation and make them cancellation-safe
  2. Increase the finish_shutdown timeouts (Duration::from_secs(1), Duration::from_secs(2)) if teardown timing is tight in your environment
  3. Retry teardown_partial_connect after the failed attempt, since finish_tasks errors are recorded in shutdown_errors and teardown still proceeds
  4. Capture the inner `{e}` value to identify which of the two groups (session vs command) failed and fix that specific task

Example fix

// before
self.session_tasks.finish_shutdown(Duration::from_secs(1), Duration::from_secs(2)),
// after
self.session_tasks.finish_shutdown(Duration::from_secs(5), Duration::from_secs(10))
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust: no pre-validation API; guard with logging on disconnect
if client.is_degraded() {
    log::warn!("Betfair data client shutting down with pending tasks; shutdown may time out");
}

Try / catch

match client.disconnect().await {
    Ok(()) => {},
    Err(e) => log::error!("Betfair teardown failed: {e:#}"), // contains 'Failed to finish Betfair data ... tasks'
}

Prevention

When it happens

Trigger: Called via finish_tasks during teardown_partial_connect (invoked from failed reconnect attempts in prepare_task_groups or disconnect). Triggered when a spawned session or command task hangs past the 1s/2s shutdown timeouts, or when finish_shutdown otherwise returns Err (e.g. task panicked or group already in a bad state).

Common situations: A stuck Betfair stream task blocked on a network read without honoring cancellation; a command task awaiting a response that never arrives; calling disconnect while tasks are mid-flight; reconnect loops where a previous generation left tasks wedged.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


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