nautechsystems/nautilus_trader · error · anyhow::Error

Invalid order side: {e}

Error message

Invalid order side: {e}

What it means

Thrown while pricing an emulated MARKET order during submit_order: the adapter converts the Nautilus OrderSide to AX's two-value side enum (B/S) via AxOrderSide::try_from. AX only encodes Buy and Sell, so any other OrderSide discriminant (NO_SIDE or UNDEFINED) fails with 'Invalid order side for AX', wrapped by this message. In practice Nautilus orders normally always carry BUY or SELL, so hitting this means an order object was built or deserialized without a side.

Source

Thrown at crates/adapters/architect_ax/src/execution.rs:228

        };

        let ws_orders = self.ws_orders.clone();
        let trader_id = self.core.trader_id;
        let emitter = self.emitter.clone();
        let clock = self.clock;

        let http_client = self.http_client.clone();

        self.spawn_task("submit_order", async move {
            // AX emulates market orders with preview-priced IOC limits, so book moves
            // between preview and submission can produce partial fills.
            let (price, submit_time_in_force, submit_post_only) = if order_type
                == OrderType::Market
            {
                let preview_result: anyhow::Result<Price> = async {
                    let symbol = instrument_id.symbol.inner();
                    let ax_side = AxOrderSide::try_from(order_side)
                        .map_err(|e| anyhow::anyhow!("Invalid order side: {e}"))?;
                    let qty_contracts = quantity_to_contracts(quantity)?;

                    let instrument = http_client.get_instrument(&symbol).ok_or_else(|| {
                        anyhow::anyhow!("Instrument {instrument_id} not found in cache")
                    })?;

                    let request =
                        PreviewAggressiveLimitOrderRequest::new(symbol, qty_contracts, ax_side);
                    let response = http_client
                        .inner
                        .preview_aggressive_limit_order(&request)
                        .await
                        .map_err(|e| {
                            anyhow::anyhow!("Failed to preview aggressive limit order: {e}")
                        })?;

                    if response.remaining_quantity > 0 {
                        log::warn!(

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Log order.order_side() before submit and fix the caller so the order is created with OrderSide::Buy or OrderSide::Sell
  2. Create orders through the Strategy's order_factory so side is always populated
  3. If the side truly is optional in your flow, submit a LIMIT order only after the side is known

Example fix

// before
let order = factory.market(
    instrument.id,
    order_side_from_signal.unwrap_or_default(), // NO_SIDE when None
    qty,
); // -> "Invalid order side"
// after
let side = order_side_from_signal
    .context("signal produced no order side")?; // fail before submit
let order = factory.market(instrument.id, side, qty);
Defensive patterns

Strategy: validation

Validate before calling

// Before submit_order for a MARKET order
use nautilus_model::enums::OrderSide;
fn side_ok(side: OrderSide) -> bool {
    matches!(side, OrderSide::Buy | OrderSide::Sell)
}
assert!(side_ok(order.order_side()), "order has no side");

Type guard

fn is_ax_supported_side(side: OrderSide) -> bool {
    matches!(side, OrderSide::Buy | OrderSide::Sell)
}

Prevention

When it happens

Trigger: Submitting a MARKET order whose order_side() is NO_SIDE/UNDEFINED (manually constructed OrderAny, corrupted order state, or a code path that creates an OrderInitialized without setting side); any future OrderSide variant added to Nautilus that the adapter has not mapped.

Common situations: Custom strategy code constructing orders directly instead of via the order factory; porting code where the side parameter was accidentally dropped; serialized/restored order objects with a default-0 (NO_SIDE) side field.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16). Data as JSON: /api/errors/6cd347dc51d426c9. Report an issue: GitHub.