nautechsystems/nautilus_trader · error · anyhow::Error

Failed to terminate Polymarket session tasks: {e}

Error message

Failed to terminate Polymarket session tasks: {e}

What it means

This error wraps a failure from `await_session_tasks`, which gracefully shuts down the Polymarket session task group (WebSocket dispatch, account-state polling) via `begin_shutdown`/`finish_shutdown` with dedicated graceful and abort timeouts. It surfaces when session tasks neither finish nor abort within those windows, or when the task group itself reports an error during shutdown.

Source

Thrown at crates/adapters/polymarket/src/execution/lifecycle.rs:226

                    && !self.fill_tracker.is_fully_filled(&venue_order_id)
                {
                    self.emitter
                        .emit_order_canceled(&order, Some(venue_order_id), cancel_ts);
                }
            }
        }

        result
            .map_err(|e| anyhow::anyhow!("Failed to terminate Polymarket execution tasks: {e}"))?;
        Ok(())
    }

    pub(super) async fn await_session_tasks(&self) -> anyhow::Result<()> {
        self.session_tasks.begin_shutdown();
        self.session_tasks
            .finish_shutdown(TASK_SESSION_GRACEFUL_SHUTDOWN_TIMEOUT, TASK_ABORT_TIMEOUT)
            .await
            .map_err(|e| anyhow::anyhow!("Failed to terminate Polymarket session tasks: {e}"))?;
        Ok(())
    }

    pub(super) async fn refresh_account_state(&self) -> anyhow::Result<()> {
        fetch_and_emit_account_state(
            &self.http_client,
            &self.emitter,
            self.clock,
            self.config.signature_type,
        )
        .await
    }

    pub(super) async fn await_account_registered(&self, timeout_secs: f64) -> anyhow::Result<()> {
        let account_id = self.core.account_id;

        if self.core.cache().account(&account_id).is_some() {
            log::info!("Account {account_id} registered");

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the wrapped `{e}` error chain to identify which session task failed
  2. Ensure the WebSocket client is disconnected before awaiting session shutdown so dispatch tasks observe closure
  3. Increase TASK_SESSION_GRACEFUL_SHUTDOWN_TIMEOUT if tasks legitimately need longer to wind down
  4. Verify no session task loops on blocking (std::thread::sleep / blocking I/O) that starves the async runtime
  5. Retry connection after abort timeout has force-terminated stuck tasks

Example fix

// before: session tasks never see WS close
self.await_session_tasks().await?;
// after: disconnect WS first so dispatch loop exits
self.ws_client.disconnect().await;
self.await_session_tasks().await?;
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure WS is disconnected before awaiting session shutdown
if adapter.is_ws_connected() {
    adapter.disconnect_ws().await;
}

Try / catch

if let Err(e) = adapter.await_session_tasks().await {
    tracing::error!("session shutdown failed: {e:#}");
}

Prevention

When it happens

Trigger: Calling await_session_tasks (from teardown_partial_connect or connect_client) while the WebSocket dispatch task or session spawner tasks hang past TASK_SESSION_GRACEFUL_SHUTDOWN_TIMEOUT, or the task group returns an error from finish_shutdown.

Common situations: WS message stream stalled without disconnecting; a session task blocked reading from a closed channel; slow machine or blocked runtime worker preventing task polling during shutdown; reconnect loop invoking connect_client while old session tasks are stuck.

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