nautechsystems/nautilus_trader · error
Architect AX orders WebSocket handler did not stop after abo
Error message
Architect AX orders WebSocket handler did not stop after abort
What it means
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.
Source
Thrown at crates/adapters/architect_ax/src/websocket/orders/client.rs:754
tokio::time::sleep(Duration::from_millis(50)).await;
self.signal.store(true, Ordering::Release);
let outcome = self
.task_handle
.finish(Duration::from_secs(2), Duration::from_secs(2))
.await;
*self.reconnect_headers.lock() = None;
if let Some(control) = &self.socket_control {
control.deregister();
}
match outcome {
None | Some(TaskJoinOutcome::Completed(()) | TaskJoinOutcome::Aborted) => Ok(()),
Some(TaskJoinOutcome::Failed(error)) => Err(anyhow::anyhow!(
"Architect AX orders WebSocket handler failed: {error}"
)),
Some(TaskJoinOutcome::Incomplete) => Err(anyhow::anyhow!(
"Architect AX orders WebSocket handler did not stop after abort"
)),
}
}
async fn send_cmd(&self, cmd: HandlerCommand) -> AxOrdersWsResult<()> {
let guard = self.cmd_tx.read().await;
guard
.send(cmd)
.map_err(|e| AxOrdersWsClientError::ChannelError(e.to_string()))
}
}
impl Drop for AxOrdersWebSocketClient {
fn drop(&mut self) {
if Arc::strong_count(&self.task_handle) == 1 && !self.task_handle.is_empty() {
self.cancellation_token.load().cancel();
self.signal.store(true, Ordering::Release);View on GitHub (pinned to 18893faf8b)
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
Defensive patterns
Strategy: try-catch
Try / catch
// Rust
if let Err(e) = orders.close().await {
tracing::error!("orders ws did not stop: {e}");
// consider aborting the underlying task handle or restarting the client
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Handler hung waiting on the exchange socket without honoring cancellation; command channel deadlock; very slow remote endpoint during shutdown.
Related errors
- Hyperliquid WebSocket handler did not stop after abort
- Lighter WebSocket handler did not stop after abort
- Architect AX data WebSocket handler did not stop after abort
- Architect AX orders WebSocket handler failed: {error}
- Failed to terminate Hyperliquid data session tasks: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/f5acd3f68ac08b47.
Report an issue: GitHub.