nautechsystems/nautilus_trader · error

Failed to terminate Betfair session tasks: {e}

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.

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)

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.