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
- Check the raw fill payload: confirm order_id is genuinely empty and the fill is not a block/final-settlement fill.
- 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.
- Exclude or reconcile fills without order_id manually with the venue.
- 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
- Skip/reconcile fills with empty order_id before parsing
- Confirm special-fill flags are set for venue-side or settlement trades
- Audit fills fetched for orders placed outside the adapter
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
- AX final-settlement fill must also be classified as a block…
- account ID differs across fill group
- AX dated contract has different quote and settlement…
- AX scale must not exceed 26 for exact percent conversion…
- AX fills applied limit must be between 0 and
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::TakerView on GitHub (pinned to 18893faf8b)