{"record":{"id":"0d10211642e0edec","repo":"nautechsystems/nautilus_trader","slug":"failed-to-send-unsubscription-after-retries-e","errorCode":null,"errorMessage":"Failed to send unsubscription after retries: {e}","messagePattern":"Failed to send unsubscription after retries: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"crates/adapters/okx/src/websocket/handler.rs","lineNumber":565,"sourceCode":"        for arg in &args {\n            log::debug!(\n                \"Unsubscribing from channel: channel={:?}, inst_id={:?}\",\n                arg.channel,\n                arg.inst_id\n            );\n        }\n\n        let message = OKXSubscription {\n            op: OKXWsOperation::Unsubscribe,\n            args,\n        };\n\n        let json_txt = serde_json::to_string(&message)\n            .map_err(|e| anyhow::anyhow!(\"Failed to serialize unsubscription: {e}\"))?;\n\n        self.send_with_retry(json_txt, Some(OKX_RATE_LIMIT_KEY_SUBSCRIPTION.as_slice()))\n            .await\n            .map_err(|e| anyhow::anyhow!(\"Failed to send unsubscription after retries: {e}\"))?;\n        Ok(())\n    }\n\n    pub(crate) fn parse_raw_message(\n        msg: tokio_tungstenite::tungstenite::Message,\n    ) -> Option<OKXWsFrame> {\n        match msg {\n            tokio_tungstenite::tungstenite::Message::Text(text) => {\n                if text == TEXT_PONG {\n                    log::trace!(\"Received pong from OKX\");\n                    return None;\n                }\n\n                if text == TEXT_PING {\n                    log::trace!(\"Received ping from OKX (text)\");\n                    return Some(OKXWsFrame::Ping);\n                }\n","sourceCodeStart":547,"sourceCodeEnd":583,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/okx/src/websocket/handler.rs#L547-L583","documentation":"In `handle_unsubscribe`, the serialized unsubscribe message is sent via `send_with_retry` under the OKX subscription rate-limit key. If every retry fails (socket closed or rate limited), the error is wrapped with this message and the channel remains subscribed server-side until disconnect.","triggerScenarios":"Unsubscribing while the WebSocket connection is down, or sends keep failing across the retry window due to OKX rate limits.","commonSituations":"Unsubscribing during a network drop or after the server already closed the socket; tearing down many subscriptions at once during shutdown.","solutions":["Treat as non-fatal during shutdown: the server drops all subscriptions when the socket closes anyway.","If you need the channel truly unsubscribed, reconnect and either resubscribe with the correct set or skip the stale channel.","Check the inner `{e}` to confirm it is a transport error rather than rate limiting."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// rust\n// during teardown, unsubscribe failure is benign: server drops state on close\nif let Err(e) = unsubscribe(arg).await {\n    log::debug!(\"unsubscribe failed (ignored during shutdown): {e}\");\n}","preventionTips":["Skip best-effort unsubscription during shutdown; closing the socket unsubscribes everything.","Only unsubscribe while the connection is confirmed active.","Check the inner error to distinguish transport loss from rate limiting."],"tags":["websocket","okx","unsubscribe","retry"],"backgroundTag":"network-request-failed","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}