{"record":{"id":"555b38065c28e3cd","repo":"nautechsystems/nautilus_trader","slug":"cannot-batch-cancel-empty-order-list","errorCode":null,"errorMessage":"Cannot batch cancel empty order list","messagePattern":"Cannot batch cancel empty order list","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/trading/src/strategy/mod.rs","lineNumber":684,"sourceCode":"    }\n\n    /// Batch cancels multiple orders for the same instrument.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the strategy is not registered, the orders span multiple instruments,\n    /// or contain emulated/local orders.\n    fn cancel_orders(\n        &mut self,\n        client_order_ids: Vec<ClientOrderId>,\n        client_id: Option<ClientId>,\n        params: Option<Params>,\n    ) -> anyhow::Result<()>\n    where\n        Self: StrategyNative,\n    {\n        if client_order_ids.is_empty() {\n            anyhow::bail!(\"Cannot batch cancel empty order list\");\n        }\n\n        let (trader_id, strategy_id, ts_init) = {\n            let core = StrategyNative::strategy_core_mut(self);\n            (\n                registered_trader_id(core)?,\n                registered_strategy_id(core)?,\n                core.clock_mut().timestamp_ns(),\n            )\n        };\n\n        // TODO: Snapshot all orders from the cache. See `cancel_order` for the rationale.\n        let orders: Vec<OrderAny> = {\n            let cache_rc = StrategyNative::strategy_core_mut(self).cache_rc();\n            let cache = cache_rc.borrow();\n            client_order_ids\n                .iter()\n                .map(|id| {","sourceCodeStart":666,"sourceCodeEnd":702,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/trading/src/strategy/mod.rs#L666-L702","documentation":"Input guard in Strategy::cancel_orders: the batch cancel was called with an empty client order ID list. A cancel request must reference at least one order, so the empty batch is rejected before any command is sent.","triggerScenarios":"Calling `strategy.cancel_orders(vec![], instrument_id, None, None)` or passing an empty filtered list of client order IDs.","commonSituations":"Filtering IDs of already-closed orders down to zero; cancel-all implementations that pass a list collected from a cache that happens to be empty; default/uninitialized vectors.","solutions":["Ensure the ID list is non-empty before calling cancel_orders","Skip the call when empty: if ids.is_empty() { return; }","Use cancel_all_orders if the intent is to cancel everything regardless of count"],"exampleFix":"// before\nstrategy.cancel_orders(&ids, instrument_id, None, None).await?;\n// after\nif !ids.is_empty() {\n    strategy.cancel_orders(&ids, instrument_id, None, None).await?;\n}","handlingStrategy":"validation","validationCode":"if client_order_ids.is_empty() {\n    return Ok(()); // or use cancel_all_orders instead\n}","typeGuard":"fn is_non_empty<T>(items: &[T]) -> bool { !items.is_empty() }","tryCatchPattern":null,"preventionTips":["Guard empty ID lists before calling","Prefer cancel_all_orders when cancelling everything","Watch for filters that drain the ID list to zero"],"tags":["validation","batch-orders","empty-input"],"backgroundTag":"empty-required-field","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}