nautechsystems/nautilus_trader · error
Batch cancel order failed: {e}
Error message
Batch cancel order failed: {e} What it means
batch_cancel_orders splits a batch into per-order spawned tasks; when an individual cancel request fails for a specific order_id/client_order_id, the error is logged and re-raised as 'Batch cancel order failed: {e}' within that order's task. Other orders in the batch continue independently, so this error is per-order, not batch-wide.
Source
Thrown at crates/adapters/deribit/src/execution.rs:1113
let trader_id = cancel.trader_id;
let strategy_id = cancel.strategy_id;
let instrument_id = cancel.instrument_id;
self.spawn_task("batch_cancel_order", async move {
if let Err(e) = ws_client
.cancel_order(
&order_id,
client_order_id,
trader_id,
strategy_id,
instrument_id,
)
.await
{
log::error!(
"Batch cancel order failed: order_id={order_id}, client_order_id={client_order_id}, error={e}"
);
anyhow::bail!("Batch cancel order failed: {e}");
}
Ok(())
});
}
Ok(())
}
}
/// Dispatches a WebSocket message using the event emitter.
fn dispatch_ws_message(message: NautilusWsMessage, emitter: &ExecutionEventEmitter) {
match message {
NautilusWsMessage::AccountState(state) => {
emitter.send_account_state(state);
}
NautilusWsMessage::OrderStatusReports(reports) => {
log::debug!("Processing {} order status report(s)", reports.len());
for report in reports {View on GitHub (pinned to 18893faf8b)
Solutions
- Match the failed order_id/client_order_id from the log to identify which orders need retry
- Check Deribit rate limits and throttle batch submissions for large order sets
- Verify each order's open state; skip orders already filled or cancelled
- Retry only the failed subset after connectivity is confirmed
Example fix
// before
if let Err(e) = ws_client.batch_cancel_orders(order_ids).await {
anyhow::bail!("Batch cancel order failed: {e}");
}
// after
for chunk in order_ids.chunks(10) {
tokio::time::sleep(Duration::from_millis(100)).await; // respect rate limits
if let Err(e) = ws_client.batch_cancel_orders(chunk.to_vec()).await {
log::warn!("partial batch cancel failed, will reconcile: {e}");
failed_ids.extend(chunk.iter().cloned());
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-filter batch to orders known open and throttle batch size let batch: Vec<_> = order_ids.iter().filter(|id| open_orders.contains(id)).collect(); assert!(batch.len() <= 20, "batch too large, risk of rate limiting");
Try / catch
match client.batch_cancel_orders(ids).await {
Ok(()) => {},
Err(e) => {
log::warn!("batch cancel partial failure: {e}");
reconcile_open_orders_against_venue();
}
} Prevention
- Reconcile local open orders against the venue before large batch cancels
- Chunk batches and add delays to respect Deribit rate limits
- Expect partial failures: treat this error as per-order and retry only failed IDs
- Purge stale order IDs after reconnects
When it happens
Trigger: Calling batch_cancel_orders where one or more individual orders cannot be cancelled — unknown/stale order ID, already filled/cancelled order, rate limiting across the batch, or connection issues.
Common situations: Large batches hitting Deribit rate limits; stale IDs from a previous session; mixed results where some cancels succeed and others fail, making reconciliation confusing.
Related errors
- Modify order failed: {e}
- Cancel order failed: {e}
- Cancel all orders failed: {e}
- {reason}
- Batch cancel limit is {endpoint_limit} orders for {product_t
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/53c4a93e6330f474.
Report an issue: GitHub.