nautechsystems/nautilus_trader · error

Failed to terminate Betfair session tasks

Error message

Failed to terminate Betfair session tasks: {e}

What it means

await_session_tasks shuts down the session-task group (login/keep-alive/heartbeat tasks) via begin_shutdown/finish_shutdown with 1s and 2s grace windows. If those tasks cannot be terminated in time or finish_shutdown errors, this anyhow error is returned.

Solutions

  1. Increase shutdown durations for slow environments
  2. Ensure all session loops observe the cancellation token so they exit promptly
  3. Call teardown/abort_session_tasks when timeouts persist, then tear down the client

Example fix

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

Strategy: try-catch

Validate before calling

if !self.session_tasks.is_open() { return Ok(()); } // nothing to shut down

Try / catch

if let Err(e) = exec_client.await_session_tasks().await {
    log::error!("session task shutdown: {e}; aborting");
    exec_client.abort_session_tasks();
}

Prevention

When it happens

Trigger: Session keep-alive or reconnect loops not terminating within 1s+2s during teardown; tasks blocked awaiting a hung HTTP call or an unobservable cancellation token.

Common situations: Shutting down while a keep-alive request to Betfair hangs; session token renewal in progress at teardown; cancellation token not propagated to the session loop.

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

Appendix: source

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

        self.session_tasks.begin_shutdown();
        self.account_refresh_tx = None;
    }

    async fn await_pending_tasks(&self) -> anyhow::Result<()> {
        self.pending_tasks.begin_shutdown();
        self.pending_tasks
            .finish_shutdown(Duration::from_secs(1), Duration::from_secs(2))
            .await
            .map_err(|e| anyhow::anyhow!("Failed to terminate Betfair execution tasks: {e}"))?;
        Ok(())
    }

    async fn await_session_tasks(&self) -> anyhow::Result<()> {
        self.session_tasks.begin_shutdown();
        self.session_tasks
            .finish_shutdown(Duration::from_secs(1), Duration::from_secs(2))
            .await
            .map_err(|e| anyhow::anyhow!("Failed to terminate Betfair session tasks: {e}"))?;
        Ok(())
    }

    async fn teardown_partial_connect(&mut self) -> anyhow::Result<()> {
        self.abort_session_tasks();
        self.abort_pending_tasks();

        if let Some(control) = &self.socket_control {
            control.deregister();
        }

        if let Some(client) = self.stream_client.as_ref() {
            match client.close().await {
                Ok(()) => self.stream_client = None,
                Err(e) => self
                    .shutdown_errors
                    .push(format!("stream shutdown failed: {e}")),
            }

View on GitHub (pinned to 18893faf8b)