nautechsystems/nautilus_trader · error

Batch cancel limit is {endpoint_limit} orders for {product_t

Error message

Batch cancel limit is {endpoint_limit} orders for {product_type}

What it means

A Bybit batch cancel request exceeded the venue's per-endpoint batch limit for the given product type (spot vs linear/inverse derivatives have different limits, e.g. linear typically caps batch cancels at 10). The request is rejected client-side before being sent.

Source

Thrown at crates/adapters/bybit/src/http/client.rs:2841

        instrument_ids: Vec<InstrumentId>,
        client_order_ids: Vec<Option<ClientOrderId>>,
        venue_order_ids: Vec<Option<VenueOrderId>>,
    ) -> anyhow::Result<Vec<OrderStatusReport>> {
        if instrument_ids.len() != client_order_ids.len()
            || instrument_ids.len() != venue_order_ids.len()
        {
            anyhow::bail!(
                "instrument_ids, client_order_ids, and venue_order_ids must have the same length"
            );
        }

        if instrument_ids.is_empty() {
            return Ok(Vec::new());
        }

        let endpoint_limit = batch_endpoint_limit(product_type);
        if instrument_ids.len() > endpoint_limit {
            anyhow::bail!(
                "Batch cancel limit is {endpoint_limit} orders for {}",
                product_type.as_str()
            );
        }

        let mut cancel_entries = Vec::new();

        for ((instrument_id, client_order_id), venue_order_id) in instrument_ids
            .iter()
            .zip(client_order_ids.iter())
            .zip(venue_order_ids.iter())
        {
            let bybit_symbol = BybitSymbol::new(instrument_id.symbol.as_str())?;
            let mut cancel_entry = BybitBatchCancelOrderEntryBuilder::default();
            cancel_entry.symbol(bybit_symbol.raw_symbol().to_string());

            if let Some(venue_order_id) = venue_order_id {
                cancel_entry.order_id(venue_order_id.to_string());

View on GitHub (pinned to d1527c24af)

Solutions

  1. Chunk the cancels into batches of at most batch_endpoint_limit(product_type) orders per request
  2. Reduce the number of instruments targeted per batch cancel call
  3. Fall back to individual cancel requests for the remainder when chunking is awkward

Example fix

// before
let reports = client.batch_cancel(instrument_ids).await?;
// after
let limit = 10; // batch_endpoint_limit(product_type)
for chunk in instrument_ids.chunks(limit) {
    let reports = client.batch_cancel(chunk.to_vec()).await?;
}
Defensive patterns

Strategy: validation

Validate before calling

const LIMIT: usize = 10; // match batch_endpoint_limit(product_type)
if instrument_ids.len() > LIMIT {
    anyhow::bail!("batch cancel of {} exceeds Bybit limit {LIMIT}; chunk the request", instrument_ids.len());
}

Prevention

When it happens

Trigger: Calling the batch cancel path (cancel_all via batch endpoint or batch_cancel_orders) on the Bybit HTTP client with more instrument_ids/orders than batch_endpoint_limit(product_type) allows.

Common situations: A strategy firing many cancels across instruments during a volatility spike gets bucketed into one oversized batch; migrating from an adapter/version with a higher limit; spot limits applied to derivatives workflows.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@d1527c24af (2026-08-27). Data as JSON: /api/errors/89dea5ae496148e9. Report an issue: GitHub.