nautechsystems/nautilus_trader · error · anyhow::Error
batch cancel orders failed
Error message
batch cancel orders failed
What it means
The OKX execution client spawns an async task to cancel open orders in bulk over the OKX private WebSocket. When `ws_private.batch_cancel_orders` returns an error (transport failure, auth issue, or venue rejection), the task wraps it with anyhow context "batch cancel orders failed". This is used by cancel_all_orders, so it means some or all pending cancel requests may not have reached OKX.
Source
Thrown at crates/adapters/okx/src/execution.rs:2593
drop(cache);
log::debug!(
"Canceling {} regular orders and {} algo orders for {}",
regular_payload.len(),
algo_orders.len(),
cmd.instrument_id
);
if !regular_payload.is_empty() {
let ws_private = self.ws_private.clone();
self.spawn_task("batch_cancel_orders", async move {
if let Err(e) = ws_private.batch_cancel_orders(regular_payload).await {
log_batch_cancel_failure(
classify_okx_ws_failure(&e),
regular_cancel_contexts.len(),
);
return Err(anyhow::Error::new(e).context("batch cancel orders failed"));
}
Ok(())
});
}
// OKX doesn't support algo cancel via private WebSocket, must use HTTP
if !algo_orders.is_empty() {
let items: Vec<_> = algo_orders
.into_iter()
.map(
|(
instrument_id,
client_order_id,
venue_order_id,
_trader_id,
strategy_id,
)| {
let request = OKXCancelAlgoOrderRequest {View on GitHub (pinned to 18893faf8b)
Solutions
- Check the OKX adapter logs for the classified failure (log_batch_cancel_failure output) to see if it is transport, auth, or venue rejection.
- Verify the private WebSocket is connected and authenticated before issuing cancels; reconnect and retry.
- Retry the batch cancel (cancels are idempotent; already-canceled orders report as such).
- Fall back to individual order cancels or the HTTP cancel path for orders that failed in the batch.
- Confirm order count is within OKX batch size limits for batch cancel.
Example fix
// before
let result = client.cancel_all_orders(&instrument_id, OrderSide::Both);
// after
match client.cancel_all_orders(&instrument_id, OrderSide::Both) {
Ok(()) => {}
Err(e) => {
tracing::error!("batch cancel failed: {e}; retrying after reconnect");
ws.reconnect().await;
client.cancel_all_orders(&instrument_id, OrderSide::Both)?;
}
} Defensive patterns
Strategy: retry
Validate before calling
// before canceling assert!(ws_private.is_connected(), "OKX private WS must be connected"); assert!(!orders.is_empty() && orders.len() <= 20, "within OKX batch limit");
Try / catch
match result {
Ok(()) => {}
Err(e) if is_transport_failure(&e) => {
backoff_retry(|| client.cancel_all_orders(&instrument_id, side)).await?;
}
Err(e) => return Err(e),
} Prevention
- Monitor WS connection state and only cancel when authenticated/connected
- Keep order state cached so already-closed orders are excluded from batches
- Retry cancels idempotently with backoff
- Log classify_okx_ws_failure categories to detect recurring transport issues
When it happens
Trigger: Calling cancel_all_orders on the OKX execution client while the private WebSocket is disconnected, reconnecting, or when OKX rejects the batch cancel request payload (e.g. sCode errors in the response for one or more orders).
Common situations: Network flaps or WebSocket drop right before canceling all orders during an emergency flatten; OKX session expired / re-login in progress; exceeding OKX batch cancel size limits; canceling orders that were already filled or canceled, causing per-order rejection in the batch response.
Understand the failure class
Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.
Related errors
- cancel order failed
- Binance Futures data teardown failed: {}
- Binance Spot data teardown failed: {}
- WS submit order failed: {e}
- WS cancel order failed: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/cfcb747ed4c9f44c.
Report an issue: GitHub.