nautechsystems/nautilus_trader · error

{reason}

Error message

{reason}

What it means

During `cancel_algo_order` (invoked from `cancel_order`), the adapter's HTTP cancel path reports a rejection reason for the algo-order cancel request; if `is_rejected` is true, the adapter aborts with the venue-provided `{reason}` as the error message. This means OKX itself refused the algo cancel, not the adapter.

Source

Thrown at crates/adapters/okx/src/execution.rs:1096

                            (code != OKX_SUCCESS_CODE)
                                .then_some((code, r.s_msg.as_deref().unwrap_or("unknown")))
                        })
                    }) {
                        let reason =
                            format!("cancel-algo-order-rejected: s_code={code}, s_msg={msg}");
                        let failure = classify_okx_venue_code(code, reason.clone());
                        let is_rejected = matches!(failure, CommandFailure::VenueRejected(_));
                        emit_cancel_failure(
                            failure,
                            Some((&emitter, clock)),
                            command.client_order_id,
                            command.instrument_id,
                            command.strategy_id,
                            command.venue_order_id,
                        );

                        if is_rejected {
                            anyhow::bail!("{reason}");
                        }
                    }
                }
            }

            Ok(())
        });
    }

    fn mass_cancel_instrument(&self, instrument_id: InstrumentId) {
        if is_spread_instrument(instrument_id) {
            let http_client = self.http_client.clone();
            self.spawn_task("mass_cancel_orders_http", async move {
                if let Err(e) = http_client.cancel_all_orders(instrument_id).await {
                    log_mass_cancel_failure(classify_okx_http_failure(&e), instrument_id);
                    return Err(anyhow::Error::new(e).context("mass cancel orders failed"));
                }
                Ok(())

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the embedded `{reason}` text — it is OKX's own rejection message (commonly 'order already canceled' or 'order has been filled').
  2. Check the order's current state in the cache before issuing a cancel; skip cancel if already closed/filled.
  3. Make cancel handling idempotent: treat 'already canceled/filled' rejections as success in your reconciliation logic.
  4. Retry with a fresh `venue_order_id` if the ID was stale after a session re-establishment.
Defensive patterns

Strategy: try-catch

Validate before calling

if let Some(order) = cache.order_for_venue(&venue_order_id) {
    if order.is_closed() {
        return Ok(()); // nothing to cancel
    }
}

Try / catch

match client.cancel_order(cmd) {
    Err(e) if e.to_string().contains("already canceled") || e.to_string().contains("filled") => {
        log::warn!("cancel rejected as already closed: {e}"); // treat as idempotent success
    }
    Err(e) => return Err(e),
    Ok(()) => {}
}

Prevention

When it happens

Trigger: Calling `cancel_order` for an algo order where the OKX cancel response marks the result as rejected; the raw OKX reason string (e.g. order already filled/canceled, invalid algo ID, order not found) becomes the error text.

Common situations: Race condition where the trigger order fires (fills) just before the cancel arrives; canceling with a stale `venue_order_id` after a reconnect; duplicate cancel requests.

Related errors


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