nautechsystems/nautilus_trader · critical · anyhow::Error
WS cancel order failed: {e}
Error message
WS cancel order failed: {e} What it means
Raised inside the spawned cancel_order_ws task when the Binance WebSocket API cancel_order_with_id call fails. The client removes the cancel's pending request, logs 'WS cancel request failed for {client_order_id}', and bails with this message wrapping the venue error. Because the cancel did not reach the exchange, the order may still be live — treat this as a risk-relevant failure, not a cleanup detail.
Source
Thrown at crates/adapters/binance/src/futures/execution.rs:845
// Pre-register before sending to avoid response racing the insert
let request_id = ws_client.next_request_id();
dispatch_state.pending_requests.insert(
request_id.clone(),
PendingRequest {
client_order_id,
venue_order_id,
operation: PendingOperation::Cancel,
},
);
self.spawn_task("cancel_order_ws", async move {
if let Err(e) = ws_client
.cancel_order_with_id(request_id.clone(), params)
.await
{
dispatch_state.pending_requests.remove(&request_id);
log::error!("WS cancel request failed for {client_order_id}: {e}");
anyhow::bail!("WS cancel order failed: {e}");
}
Ok(())
});
return;
}
let http_client = self.http_client.clone();
self.spawn_task("cancel_order", async move {
let result = if use_algo_cancel {
// Try algo cancel first; if it fails, the order may have been triggered
// before this session started, so fall back to regular cancel
match http_client.cancel_algo_order(client_order_id).await {
Ok(()) => Ok(()),
Err(algo_err) => {
log::debug!("Algo cancel failed, trying regular cancel: {algo_err}");
http_clientView on GitHub (pinned to a4b06ed870)
Solutions
- Read the embedded WS error code in the logged {e}: -2011 means the order is already filled/cancelled and the state should be reconciled, not retried
- Verify the order is actually open (order status reports) before retrying the cancel
- Throttle cancels within venue limits; batch cancel where available
- If WS errors persist, check connection health or use the HTTP cancel path if configured
Defensive patterns
Strategy: try-catch
Validate before calling
// Only attempt a cancel when the order is live:
if matches!(order.status(), OrderStatus::Accepted | OrderStatus::Triggered | OrderStatus::PendingUpdate) {
client.cancel_order(order.clone()).await?;
} Try / catch
// cancel failures are async: on OrderCanceled absence + 'WS cancel request failed' log, // - error -2011 (unknown order): reconcile status, do NOT retry // - rate limit / transient: retry once after backoff // - still live after retry: escalate (flatten via reduce-only order) per risk policy
Prevention
- Track expected order state and reconcile with order-status reports after cancel failures
- Throttle cancel bursts inside venue limits
- Prefer batch-cancel APIs for teardowns
- Treat a failed cancel as a live-order incident, not a logging event
When it happens
Trigger: Cancelling over the WS order channel with an unknown/already-filled order id (-2011 Unknown order sent), during a WS reconnect window, under cancel-rate limits, or with params rejected by the venue (e.g. cancel-replace race already resolved).
Common situations: Cancel bursts during volatility tripping rate limits; cancels racing order fills; WS session expiry between submit and cancel; reconciliation loops cancelling stale orders that no longer exist.
Related errors
- WS submit order failed: {e}
- WS cancel order failed: {e}
- BacktestNode state is unavailable when dispose_on_completion
- Timeout waiting for account {account_id} to be registered af
- noid '{}' does not match new order oid '{}'
AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16).
Data as JSON: /api/errors/ed8d26dd84415098.
Report an issue: GitHub.