{"record":{"id":"73eceefbe336d4bb","repo":"nautechsystems/nautilus_trader","slug":"failed-to-send-unsubscribemarket-e","errorCode":null,"errorMessage":"Failed to send UnsubscribeMarket: {e}","messagePattern":"Failed to send UnsubscribeMarket: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/polymarket/src/websocket/client.rs","lineNumber":87,"sourceCode":"}\n\nimpl WsSubscriptionHandle {\n    /// Sends a market subscribe command to the handler.\n    pub async fn subscribe_market(&self, asset_ids: Vec<String>) -> anyhow::Result<()> {\n        self.cmd_tx\n            .read()\n            .await\n            .send(HandlerCommand::SubscribeMarket(asset_ids))\n            .map_err(|e| anyhow::anyhow!(\"Failed to send SubscribeMarket: {e}\"))\n    }\n\n    /// Sends a market unsubscribe command to the handler.\n    pub async fn unsubscribe_market(&self, asset_ids: Vec<String>) -> anyhow::Result<()> {\n        self.cmd_tx\n            .read()\n            .await\n            .send(HandlerCommand::UnsubscribeMarket(asset_ids))\n            .map_err(|e| anyhow::anyhow!(\"Failed to send UnsubscribeMarket: {e}\"))\n    }\n\n    // Constructs a handle around a raw command sender. Test-only: lets unit\n    // tests observe the commands the handle emits without spinning up the real\n    // feed handler.\n    #[cfg(test)]\n    pub(crate) fn from_sender(sender: tokio::sync::mpsc::UnboundedSender<HandlerCommand>) -> Self {\n        Self {\n            cmd_tx: Arc::new(tokio::sync::RwLock::new(sender)),\n        }\n    }\n}\n\n/// Provides a WebSocket client for the Polymarket CLOB API.\n///\n/// A single instance targets one channel (market or user). Use\n/// [`PolymarketWebSocketClient::new_market`] for public market data and\n/// [`PolymarketWebSocketClient::new_user`] for authenticated order/trade streams.","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/polymarket/src/websocket/client.rs#L69-L105","documentation":"unsubscribe_market() enqueues HandlerCommand::UnsubscribeMarket on the internal mpsc channel to the feed-handler task. It fails only when the receiver has been dropped because the handler task has exited, meaning there is no active session to process the unsubscribe.","triggerScenarios":"Calling unsubscribe_market() after disconnect(), after the handler task terminated (panic or fatal connection error), or concurrently with a reconnect that replaces the handler task.","commonSituations":"Cleanup/shutdown code unsubscribing after the client was already disconnected; handler died earlier from a network outage; test harness dropping the client while an unsubscribe is in flight.","solutions":["Guard the call with a connected/is_active check","If the client is down, the subscription is already gone — skip unsubscribe_market() and return Ok","Call connect() first if you need the client live again, then unsubscribe","Investigate why the handler task exited via its logs"],"exampleFix":"// before\nclient.unsubscribe_market(asset_ids).await?;\n// after\nif client.is_active() {\n    client.unsubscribe_market(asset_ids).await?;\n} // else: subscription died with the session, nothing to unsubscribe","handlingStrategy":"try-catch","validationCode":"if client.is_active() {\n    client.unsubscribe_market(asset_ids).await?;\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = client.unsubscribe_market(asset_ids).await {\n    // handler gone means the venue subscription is gone too; safe to ignore on shutdown\n    log::debug!(\"unsubscribe skipped, handler not running: {e:#}\");\n}","preventionTips":["Skip unsubscribe when the client is already disconnected — nothing to unsubscribe","Centralize lifecycle (connect/disconnect/subscribe) in one owning task","Treat this error as benign during shutdown paths","Monitor handler task liveness to distinguish real failures from benign ones"],"tags":["rust","websocket","channel","polymarket"],"backgroundTag":"channel-closed","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}