nautechsystems/nautilus_trader · error · anyhow::Error

AX regular fill has an empty order_id

Error message

AX regular fill has an empty order_id

What it means

For a regular (non-special) AX fill, the venue order_id is required and must be non-empty, because the adapter maps it directly to a VenueOrderId. An empty order_id means the fill cannot be attributed to any order, so parsing is rejected rather than silently dropping the fill.

Solutions

  1. Check the raw fill payload: confirm order_id is genuinely empty and the fill is not a block/final-settlement fill.
  2. If the fill is actually a special fill, ensure the venue sets is_block_trade/is_final_settlement correctly so it takes the synthetic-ID path.
  3. Exclude or reconcile fills without order_id manually with the venue.
  4. If the venue legitimately omits order_id for some fill types, extend the adapter to classify them as special fills.

Example fix

// before (payload)
{ "trade_id": "T9", "order_id": "", "is_block_trade": null, "is_final_settlement": null }
// after
{ "trade_id": "T9", "order_id": "ord-12345", "is_block_trade": null, "is_final_settlement": null }
Defensive patterns

Strategy: validation

Validate before calling

fn fill_has_order_id(f: &AxFill) -> bool {
    f.is_block_trade == Some(true) || f.is_final_settlement == Some(true)
        || f.order_id.as_deref().map_or(false, |s| !s.is_empty())
}

Prevention

When it happens

Trigger: parse_fill_report (via request_fill_reports) on a fill where is_block_trade and is_final_settlement are not Some(true) (so it takes the regular-fill path), fill.order_id is present but an empty string.

Common situations: Venue returns fills for orders placed outside the adapter (e.g. via the AX web UI) without an order_id, or empty-string order_id in the payload for unusual trade types; partially migrated venue data.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at crates/adapters/architect_ax/src/http/parse.rs:666

    let trade_id = TradeId::new_checked(&fill.trade_id).context("Invalid trade_id in Ax fill")?;
    let is_block_trade = fill.is_block_trade;
    let is_final_settlement = fill.is_final_settlement;
    anyhow::ensure!(
        !(is_final_settlement == Some(true) && is_block_trade == Some(false)),
        "AX final-settlement fill must also be classified as a block trade"
    );

    let is_special_fill = is_block_trade == Some(true) || is_final_settlement == Some(true);
    let venue_order_id = if is_special_fill {
        VenueOrderId::new_checked(format!("AX-FILL-{}", fill.trade_id))
            .context("Invalid synthetic venue order ID for AX fill")?
    } else {
        let order_id = fill
            .order_id
            .as_deref()
            .context("AX fill is missing order_id and explicit special-fill classification")?;
        anyhow::ensure!(
            !order_id.is_empty(),
            "AX regular fill has an empty order_id"
        );
        VenueOrderId::new_checked(order_id).context("Invalid order_id in AX fill")?
    };

    // Use explicit side field from fill
    let order_side = OrderSide::from(fill.side);

    let last_px = decimal_to_price_dp(fill.price, instrument.price_precision(), "fill.price")?;
    let last_qty = Quantity::new(fill.quantity as f64, instrument.size_precision());

    let currency = Currency::USD();
    let commission = Money::from_decimal(fill.fee, currency)
        .context("Failed to convert fill.fee Decimal to Money")?;

    let liquidity_side = if fill.is_taker {
        LiquiditySide::Taker

View on GitHub (pinned to 18893faf8b)