nautechsystems/nautilus_trader · error

Order cancellation failed: {status}

Error message

Order cancellation failed: {status}

What it means

After sending a cancel request, Kraken Futures returned result != Success and the cancel_status.status string describes the failure (e.g. 'unknown order', 'already canceled'). The client surfaces that venue-side status verbatim as an anyhow error.

Source

Thrown at crates/adapters/kraken/src/http/futures/client.rs:2326

        client_order_id: Option<ClientOrderId>,
        venue_order_id: Option<VenueOrderId>,
    ) -> anyhow::Result<()> {
        let _ = self
            .get_cached_instrument(&instrument_id.symbol.inner())
            .ok_or_else(|| InstrumentLookupError::not_found(instrument_id))?;

        let order_id = venue_order_id.as_ref().map(|id| id.to_string());
        let cli_ord_id = client_order_id.as_ref().map(truncate_cl_ord_id);

        if order_id.is_none() && cli_ord_id.is_none() {
            anyhow::bail!("Either client_order_id or venue_order_id must be provided");
        }

        let response = self.inner.cancel_order(order_id, cli_ord_id).await?;

        if response.result != KrakenApiResult::Success {
            let status = &response.cancel_status.status;
            anyhow::bail!("Order cancellation failed: {status}");
        }

        Ok(())
    }

    /// Cancels multiple orders on the Kraken Futures exchange.
    ///
    /// Automatically chunks requests into batches of 50 orders.
    ///
    /// # Parameters
    /// - `venue_order_ids` - List of venue order IDs to cancel.
    ///
    /// # Returns
    /// The total number of successfully cancelled orders.
    pub async fn cancel_orders_batch(
        &self,
        venue_order_ids: Vec<VenueOrderId>,
    ) -> anyhow::Result<usize> {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the embedded status in the message: treat 'unknown order'/'notFound' and 'already canceled' as benign idempotent outcomes and handle them non-fatally.
  2. Confirm the venue_order_id/client_order_id matches a live order via order status before cancelling.
  3. Check exchange message status / websocket fills to see if the order completed before the cancel arrived.
  4. Retry only on transient statuses; otherwise stop cancelling and reconcile position state.

Example fix

// before: treat every failure as fatal
client.cancel_order(instrument_id, None, Some(cl_ord_id)).await?;

// after: tolerate already-cancelled/unknown orders
match client.cancel_order(instrument_id, None, Some(cl_ord_id)).await {
    Ok(()) => {}
    Err(e) if e.to_string().contains("already canceled") || e.to_string().contains("unknown") => {}
    Err(e) => return Err(e),
}
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm the order is still open via status before cancelling
let status = client.get_order_status(instrument_id, venue_order_id).await?;
if status.is_terminal() { return Ok(()); }

Type guard

fn is_benign_cancel_failure(msg: &str) -> bool {
    msg.contains("unknown order") || msg.contains("already canceled") || msg.contains("notFound")
}

Try / catch

match client.cancel_order(instrument_id, vid, cid).await {
    Err(e) if is_benign_cancel_failure(&e.to_string()) => { /* idempotent: already done */ }
    Err(e) => return Err(e),
    Ok(_) => {}
}

Prevention

When it happens

Trigger: Cancelling an order that was already filled or already cancelled; cancelling with a wrong/expired order id; venue rejection due to auth or rate issues reported in the cancel status; race where the order triggers between submit of cancel and venue processing.

Common situations: Aggressive strategies cancelling stale orders; retries after a network blip cancel twice; a strategy canceling an order that the exchange already matched; clock or reconciliation drift causing stale ids.

Related errors


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