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

  1. Match the failed order_id/client_order_id from the log to identify which orders need retry
  2. Check Deribit rate limits and throttle batch submissions for large order sets
  3. Verify each order's open state; skip orders already filled or cancelled
  4. 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

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/53c4a93e6330f474. Report an issue: GitHub.