{"record":{"id":"f5acd3f68ac08b47","repo":"nautechsystems/nautilus_trader","slug":"architect-ax-orders-websocket-handler-did-not-stop","errorCode":null,"errorMessage":"Architect AX orders WebSocket handler did not stop after abort","messagePattern":"Architect AX orders WebSocket handler did not stop after abort","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/architect_ax/src/websocket/orders/client.rs","lineNumber":754,"sourceCode":"        tokio::time::sleep(Duration::from_millis(50)).await;\n        self.signal.store(true, Ordering::Release);\n\n        let outcome = self\n            .task_handle\n            .finish(Duration::from_secs(2), Duration::from_secs(2))\n            .await;\n        *self.reconnect_headers.lock() = None;\n\n        if let Some(control) = &self.socket_control {\n            control.deregister();\n        }\n\n        match outcome {\n            None | Some(TaskJoinOutcome::Completed(()) | TaskJoinOutcome::Aborted) => Ok(()),\n            Some(TaskJoinOutcome::Failed(error)) => Err(anyhow::anyhow!(\n                \"Architect AX orders WebSocket handler failed: {error}\"\n            )),\n            Some(TaskJoinOutcome::Incomplete) => Err(anyhow::anyhow!(\n                \"Architect AX orders WebSocket handler did not stop after abort\"\n            )),\n        }\n    }\n\n    async fn send_cmd(&self, cmd: HandlerCommand) -> AxOrdersWsResult<()> {\n        let guard = self.cmd_tx.read().await;\n        guard\n            .send(cmd)\n            .map_err(|e| AxOrdersWsClientError::ChannelError(e.to_string()))\n    }\n}\n\nimpl Drop for AxOrdersWebSocketClient {\n    fn drop(&mut self) {\n        if Arc::strong_count(&self.task_handle) == 1 && !self.task_handle.is_empty() {\n            self.cancellation_token.load().cancel();\n            self.signal.store(true, Ordering::Release);","sourceCodeStart":736,"sourceCodeEnd":772,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/architect_ax/src/websocket/orders/client.rs#L736-L772","documentation":"Task-teardown failure in the Architect AX orders WebSocket client's close: after signaling abort and joining the handler task, the join outcome was Failed, meaning the background handler itself errored while shutting down rather than completing or being aborted cleanly.","triggerScenarios":"Calling close() on the orders client while the orders handler is stuck (blocked await, non-cancelling loop, deadlock) and never reaches completion, failure, or abort-acknowledgement.","commonSituations":"Handler hung waiting on the exchange socket without honoring cancellation; command channel deadlock; very slow remote endpoint during shutdown.","solutions":["Audit the orders handler loop for cancellation-token/abort handling around blocking awaits","Add tokio::select! with the abort signal in long-running waits inside the handler","Check for deadlock between command senders and the handler","If reproducible, file a bug: a leaked handler task can hold the runtime or socket open"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Rust\nif let Err(e) = orders.close().await {\n    tracing::error!(\"orders ws did not stop: {e}\");\n    // consider aborting the underlying task handle or restarting the client\n}","preventionTips":["Honor the abort/cancellation signal in all handler awaits via select!","Bound every network wait with a timeout","Avoid circular channel dependencies that can deadlock handler and command paths","Add shutdown tests that simulate a hung exchange connection"],"tags":["websocket","orders","async","timeout"],"backgroundTag":"task-did-not-stop-after-abort","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"}