nautechsystems/nautilus_trader · error · anyhow::Error

No order returned in cancel response

Error message

No order returned in cancel response

What it means

cancel_order requested a cancel via BitMEX DELETE /api/v1/order but the API returned an empty array of orders, so there is nothing to parse into an OrderStatusReport. The adapter treats an empty cancel response as an error because a successful cancel echoes back the cancelled order.

Source

Thrown at crates/adapters/bitmex/src/http/client.rs:1821

    ) -> anyhow::Result<OrderStatusReport> {
        let mut params = super::query::DeleteOrderParamsBuilder::default();
        params.text(NAUTILUS_TRADER);

        if let Some(venue_order_id) = venue_order_id {
            params.order_id(vec![venue_order_id.as_str().to_string()]);
        } else if let Some(client_order_id) = client_order_id {
            params.cl_ord_id(vec![client_order_id.as_str().to_string()]);
        } else {
            anyhow::bail!("Either client_order_id or venue_order_id 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 order = orders
            .into_iter()
            .next()
            .ok_or_else(|| anyhow::anyhow!("No order returned in cancel response"))?;

        let instrument = self.instrument_from_cache(instrument_id.symbol.inner())?;
        let ts_init = self.generate_ts_init();

        parse_order_status_report(&order, &instrument, &self.order_type_cache, ts_init)
    }

    /// Cancel multiple orders.
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - Credentials are missing.
    /// - The request fails.
    /// - The order doesn't exist.
    /// - The API returns an error.
    pub async fn cancel_orders(
        &self,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the order exists and is open (e.g. via query_order/request_order_status_report) before cancelling, or treat empty cancel responses as already-cancelled.
  2. Verify the client_order_id/venue_order_id corresponds to a live order on the same environment (testnet vs mainnet) and API key.
  3. If a cancel retry produced this, treat it as success-if-cancelled: look the order up and confirm Canceled status instead of failing.
  4. Log the exact order id used and confirm with a direct GET /api/v1/order filter query.

Example fix

// before
let order = orders.into_iter().next().ok_or_else(|| anyhow::anyhow!("No order returned in cancel response"))?;
// after
let order = match orders.into_iter().next() {
    Some(o) => o,
    None => anyhow::bail!(
        "Cancel returned no order for client_order_id={client_order_id:?} venue_order_id={venue_order_id:?} (already cancelled/filled or not found)"
    ),
};
Defensive patterns

Strategy: try-catch

Validate before calling

let open = client.query_order(instrument_id, Some(cl_id), None).await?;
if open.is_none() {
    // order already gone; skip cancel
}

Type guard

fn first_order(orders: Vec<BitmexOrder>) -> Result<BitmexOrder, String> {
    orders.into_iter().next().ok_or_else(|| "empty cancel response".to_string())
}

Try / catch

match client.cancel_order(instrument_id, Some(cl_id), None).await {
    Ok(report) => handle(report),
    Err(e) if e.to_string().contains("No order returned") => {
        // treat as already-cancelled: confirm via query_order before alerting
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling cancel_order with a client_order_id or venue_order_id that does not exist on BitMEX, that was already cancelled/filled, or that belongs to a different account/symbol; the order being cancelled concurrently by another process between check and cancel.

Common situations: Re-submitting a cancel after a timeout (idempotency retry on an already-cancelled order); stale VenueOrderId after a disconnect; cancelling during settlement or when the order was rejected on the venue; testnet vs mainnet credential mismatch so the order is not visible to the API key.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


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