nautechsystems/nautilus_trader · error · anyhow::Error
client_order_ids cannot be empty
Error message
client_order_ids cannot be empty
What it means
Raised by cancel_orders when the caller supplies Some(client_order_ids) with an empty Vec (and no venue_order_ids). BitMEX requires at least one clOrdID to cancel; an empty list would be rejected or cancel nothing. The adapter fails fast locally instead of sending a doomed request.
Source
Thrown at crates/adapters/bitmex/src/http/client.rs:1861
) -> anyhow::Result<Vec<OrderStatusReport>> {
let mut params = super::query::DeleteOrderParamsBuilder::default();
params.text(NAUTILUS_TRADER);
// BitMEX API requires either client order IDs or venue order IDs, not both
// Prioritize venue order IDs if both are provided
if let Some(venue_order_ids) = venue_order_ids {
if venue_order_ids.is_empty() {
anyhow::bail!("venue_order_ids cannot be empty");
}
params.order_id(
venue_order_ids
.iter()
.map(|id| id.to_string())
.collect::<Vec<_>>(),
);
} else if let Some(client_order_ids) = client_order_ids {
if client_order_ids.is_empty() {
anyhow::bail!("client_order_ids cannot be empty");
}
params.cl_ord_id(
client_order_ids
.iter()
.map(|id| id.to_string())
.collect::<Vec<_>>(),
);
} else {
anyhow::bail!("Either client_order_ids or venue_order_ids must be provided");
}
let params = params.build().map_err(|e| anyhow::anyhow!(e))?;
let orders: Vec<BitmexOrder> = self.inner.cancel_orders_response(params).await?;
let ts_init = self.generate_ts_init();
let instrument = self.instrument_from_cache(instrument_id.symbol.inner())?;
View on GitHub (pinned to 18893faf8b)
Solutions
- Only pass Some(client_order_ids) when the Vec is non-empty
- Validate at the call site and skip the request when the list is empty
- Switch to venue_order_ids or use the cancel_all_orders endpoint if the intent was to cancel everything
Example fix
// before
client.cancel_orders(Some(cl_ord_ids), None).await?;
// after
if cl_ord_ids.is_empty() { return Ok(()); }
client.cancel_orders(Some(cl_ord_ids), None).await?; Defensive patterns
Strategy: validation
Validate before calling
if let Some(ids) = &client_order_ids {
if ids.is_empty() { return Err(anyhow!("client_order_ids must not be empty")); }
}
client.cancel_orders(client_order_ids, venue_order_ids).await?; Type guard
fn non_empty(ids: &Option<Vec<ClientOrderId>>) -> bool {
ids.as_ref().is_some_and(|v| !v.is_empty())
} Try / catch
match client.cancel_orders(client_order_ids, venue_order_ids).await {
Ok(reports) => { /* handle */ }
Err(e) if e.to_string().contains("cannot be empty") => { /* fix inputs and retry */ }
Err(e) => return Err(e),
} Prevention
- Skip the cancel call entirely when the collected ID list is empty
- Remember the endpoint is not 'cancel all' — an empty list means nothing
- Unit-test ID collection logic to ensure filters do not silently drop all IDs
When it happens
Trigger: Calling cancel_orders with client_order_ids=Some(vec![]) and venue_order_ids=None. Common when client order IDs are collected from a filtered order set that yields no matches.
Common situations: Cancelling orders by client order ID after a filter (e.g. by instrument or strategy) that removed all entries; migrating code that assumed empty means 'cancel all'.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- venue_order_ids cannot be empty
- `peg_offset_value` requires `peg_price_type`
- Pegged orders only supported for LIMIT order type, was {orde
- BitMEX only supports PRICE trailing offset type, was {offset
- `peg_offset_value` requires `peg_price_type`
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/ecdebb1fa125b4b1.
Report an issue: GitHub.