nautechsystems/nautilus_trader · error

AX regular fill has an empty order_id

Error message

AX regular fill has an empty order_id

What it means

A fill that is neither a block trade nor a final settlement is treated as a regular order fill and must carry a non-empty order_id, which becomes the VenueOrderId linking the fill to its order. Missing order_id fails earlier with a context error; an empty-string order_id fails here. Special fills instead get the synthetic id "AX-FILL-{trade_id}".

Source

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

    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 = fill.side.into();

    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 a4b06ed870)

Solutions

  1. Locate the fill by trade_id in the raw /fills response and inspect order_id and both classification flags
  2. If the fill is actually special (block/settlement), set the flags upstream so it takes the synthetic-id path
  3. If it is a regular fill, the venue must supply the order id — report to AX; the adapter cannot invent a linkage
  4. Quarantine the affected time window and re-pull fills once the upstream data is corrected

Example fix

// before (AX fill payload)
{"trade_id": "T2", "order_id": "", "is_block_trade": null, "is_final_settlement": null}
// after
{"trade_id": "T2", "order_id": "OID-123", "is_block_trade": null, "is_final_settlement": null}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check linkage before parsing (parse_fill_report is pub)
fn fill_linkage_ok(fill: &AxFill) -> bool {
    let special = fill.is_block_trade == Some(true) || fill.is_final_settlement == Some(true);
    special || fill.order_id.as_deref().map(|s| !s.is_empty()).unwrap_or(false)
}

for fill in &fills {
    if !fill_linkage_ok(fill) {
        log::error!("fill {} lacks order linkage and special classification", fill.trade_id);
        continue;
    }
    reports.push(parse_fill_report(fill, account_id, &instrument, ts_init)?);
}

Try / catch

match parse_fill_report(fill, account_id, &instrument, ts_init) {
    Ok(report) => reports.push(report),
    Err(e) if e.to_string().contains("empty order_id") => {
        log::error!("quarantining regular fill {} without order_id: {e}", fill.trade_id);
        quarantined.push(fill.trade_id.clone()); // needs venue-side fix before re-import
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: The venue emits a fill with order_id: "" (or omitted then defaulted to empty) while both special flags are false/absent — e.g. OTC or block-style fills whose classification flags were not set, or an API change dropping order linkage for some fill types.

Common situations: Block/OTC executions flowing through without is_block_trade set, so they are treated as regular fills; fixture data with placeholder empty order ids; partial API outages where fills are written before their order reference.

Related errors


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