nautechsystems/nautilus_trader · error · anyhow::Error

failed to register Polymarket message handler: {e}

Error message

failed to register Polymarket message handler: {e}

What it means

Thrown when tasks.spawn(future) fails after the WS message-handler loop closure was built, meaning the task group refused to register the background task that processes incoming WebSocket messages. Without this handler, no market data would flow, so connect() fails fast.

Source

Thrown at crates/adapters/polymarket/src/data/lifecycle.rs:154

                            Some(msg) => handle_ws_message(msg, &ctx),
                            None => {
                                log::debug!("WebSocket message channel closed");
                                break;
                            }
                        }
                    }
                    () = cancellation.cancelled() => {
                        log::debug!("Polymarket message handler cancelled");
                        break;
                    }
                }
            }

            log::debug!("Polymarket message handler ended");
        };
        self.tasks
            .spawn(future)
            .map_err(|e| anyhow::anyhow!("failed to register Polymarket message handler: {e}"))?;
        Ok(())
    }

    pub(super) fn register_resolve_poll_task(&self) -> anyhow::Result<()> {
        let cancellation = self.cancellation_token.clone();
        let gamma_client = self.provider.http_client().clone();
        let clob_public_client = self.clob_public_client.clone();
        let clock = self.clock;
        let resolve_poll_enabled = self.config.resolve_poll_enabled;
        let subscribe_new_markets = self.config.subscribe_new_markets;
        let interval_secs = self.config.resolve_poll_interval_secs.max(1);
        let grace_secs = self.config.resolve_poll_grace_secs;
        let max_wait_secs = self.config.resolve_poll_max_wait_secs.max(grace_secs);
        let instruments = self.instruments.clone();
        let token_meta = self.token_meta.clone();
        let order_books = self.order_books.clone();
        let last_quotes = self.last_quotes.clone();
        let active_quote_subs = self.active_quote_subs.clone();

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Avoid concurrent disconnect during connect; await lifecycle calls sequentially
  2. Retry connect(); a new task generation is started when the group is not open
  3. Confirm the tokio runtime outlives the client connect call
Defensive patterns

Strategy: try-catch

Try / catch

match client.connect().await {
    Err(e) if e.to_string().contains("failed to register Polymarket message handler") => {
        // generation was closed; rebuild or retry
        client = build_client(cfg)?;
        client.connect().await?;
    }
    r => r?,
}

Prevention

When it happens

Trigger: tasks.spawn() returns Err during register_message_handler — typically because the task group is closed (shutdown started) or the runtime is terminating between obtaining the spawner and spawning the task.

Common situations: Disconnect racing connect; runtime dropped mid-connect; task group admission window closed by an earlier failed registration in the same connect sequence.

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