nautechsystems/nautilus_trader · error · anyhow::Error
failed to batch cancel orders
Error message
failed to batch cancel orders
What it means
Polymarket's batch_cancel_orders_command submits a batch of order cancels to the CLOB API. When the batch HTTP call returns Err, each order is marked failed via apply_cancel_http_failure and the task returns "failed to batch cancel orders". The canceled/not_canceled split in the response is the success path; this is the outright call-failure path.
Source
Thrown at crates/adapters/polymarket/src/execution/cancellations.rs:504
match submitter.cancel_orders(&order_id_refs).await {
Ok(response) => {
for (venue_id_str, order) in &venue_to_order {
let vid = VenueOrderId::from(venue_id_str.as_str());
process_cancel_result(&response, venue_id_str, order, vid, &emitter, clock);
}
log::debug!("Batch canceled {} orders", response.canceled.len());
Ok(())
}
Err(e) => {
let orders: Vec<(VenueOrderId, OrderAny)> = venue_to_order
.iter()
.map(|(venue_id_str, order)| {
(VenueOrderId::from(venue_id_str.as_str()), order.clone())
})
.collect();
apply_cancel_http_failure(&e, &orders, &emitter, clock);
Err(anyhow::Error::new(e).context("failed to batch cancel orders"))
}
}
});
}
fn cancel_venue_order_id(&self, order: &OrderAny) -> Option<VenueOrderId> {
self.order_contexts
.venue_order_id(&order.client_order_id())
.or_else(|| order.venue_order_id())
.or_else(|| {
self.core
.cache()
.venue_order_id(&order.client_order_id())
.copied()
})
}
}
View on GitHub (pinned to 18893faf8b)
Solutions
- Inspect the underlying error for the CLOB HTTP status and error payload.
- Pre-filter the batch to live, open orders only; drop already-canceled/filled ids.
- Verify API credentials and signer nonce are current.
- Retry the batch with backoff; on repeated failure, split into smaller batches or per-order cancels.
- Confirm batch size is within Polymarket's API limits.
Example fix
// before
let ids: Vec<_> = all_orders.iter().map(|o| o.client_order_id()).collect();
client.batch_cancel_orders(&ids)?;
// after
let ids: Vec<_> = all_orders.iter()
.filter(|o| o.status() == OrderStatus::Accepted)
.map(|o| o.client_order_id()).collect();
for chunk in ids.chunks(50) { client.batch_cancel_orders(chunk)?; } Defensive patterns
Strategy: validation
Validate before calling
// only live orders, within batch limits
let ids: Vec<_> = orders.iter()
.filter(|o| o.status() == OrderStatus::Accepted)
.map(|o| o.client_order_id()).collect();
assert!(ids.len() <= MAX_BATCH, "batch too large"); Try / catch
match client.batch_cancel_orders(&ids) {
Ok(()) => {}
Err(e) => {
warn!("batch cancel failed: {e}; splitting batch");
for half in ids.chunks(ids.len().div_ceil(2).max(1)) {
retry_with_backoff(|| client.batch_cancel_orders(half));
}
}
} Prevention
- Pre-filter batches to open, live orders
- Keep batches small and within venue limits
- Verify credentials/nonce freshness before batch calls
- Retry with backoff; escalate to per-order cancels on repeated failure
When it happens
Trigger: Calling batch_cancel_orders with client_order_ids when the CLOB batch endpoint errors: authentication/nonce failure, malformed batch payload, network error, or venue rejection of the whole request.
Common situations: Batch containing stale or already-terminal order ids; credential desync after restart; exceeding Polymarket batch size limits; transient CLOB outage during volatile markets.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- cancel order failed
- failed to cancel all orders
- Batch cancel failed: {error_msg}
- Failed to probe market closure for {failed_chunks} of {total
- All {total} event slug requests failed
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/ebc458ce80ff163c.
Report an issue: GitHub.