nautechsystems/nautilus_trader · error · anyhow::Error

Failed to start Polymarket task generation: {e}

Error message

Failed to start Polymarket task generation: {e}

What it means

`connect_client` lazily starts a new generation of the pending (execution) task group when it is not open. Before restarting, it drains prior tasks via await_pending_tasks. If `pending_tasks.start_generation()` fails (the task group runtime rejects the restart — e.g. still shutting down or already running), this error wraps the cause.

Source

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

    }

    pub(super) async fn connect_client(&mut self) -> anyhow::Result<()> {
        if self.core.is_connected() && self.pending_tasks.is_open() && self.session_tasks.is_open()
        {
            return Ok(());
        }

        log::info!("Connecting Polymarket execution client");

        if !self.pending_tasks.is_open() || !self.session_tasks.is_open() {
            self.teardown_partial_connect().await?;
        }

        if !self.pending_tasks.is_open() {
            self.await_pending_tasks().await?;
            self.pending_tasks
                .start_generation()
                .map_err(|e| anyhow::anyhow!("Failed to start Polymarket task generation: {e}"))?;
        }

        if !self.session_tasks.is_open() {
            self.await_session_tasks().await?;
            self.session_tasks.start_generation().map_err(|e| {
                anyhow::anyhow!("Failed to start Polymarket session generation: {e}")
            })?;
        }
        self.stopping.store(false, Ordering::Release);
        let ws_shutdown = self.ws_client.shutdown_handle();
        let stopping = Arc::clone(&self.stopping);
        let setup_guard =
            TaskGroupGuard::new(&[&self.session_tasks, &self.pending_tasks], move || {
                stopping.store(true, Ordering::Release);
                ws_shutdown.begin_shutdown();
            });

        let version = self

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Wait for the pending task group's shutdown/abort to fully complete before reconnecting (increase abort timeout if needed)
  2. Inspect the wrapped `{e}` for the task-group's rejection reason
  3. Avoid rapid connect/disconnect cycles; add backoff between reconnect attempts
  4. Recreate the execution client if the task group is permanently wedged

Example fix

// before: immediate reconnect loop
loop { adapter.connect().await?; }
// after: back off between generations
loop {
    adapter.connect().await?;
    tokio::time::sleep(Duration::from_secs(1)).await;
}
Defensive patterns

Strategy: retry

Validate before calling

// add reconnect backoff to avoid racing previous generation shutdown
let delay = Duration::from_millis(500 * attempts);

Try / catch

match adapter.connect().await {
    Err(e) if e.to_string().contains("task generation") => {
        tokio::time::sleep(backoff).await;
        adapter.connect().await?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: First connect or reconnect when pending_tasks is closed; start_generation called while the previous generation has not fully terminated (await_pending_tasks returned but the group still rejects a new generation).

Common situations: Rapid reconnect loops where the abort timeout has not yet completed; task-group runtime misconfiguration; calling connect after a failed teardown left the group in a half-shutdown 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/eb982513a7cebf5a. Report an issue: GitHub.