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::TakerView on GitHub (pinned to a4b06ed870)
Solutions
- Locate the fill by trade_id in the raw /fills response and inspect order_id and both classification flags
- If the fill is actually special (block/settlement), set the flags upstream so it takes the synthetic-id path
- If it is a regular fill, the venue must supply the order id — report to AX; the adapter cannot invent a linkage
- 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
- Ensure integrations that write fills always carry the originating order_id for regular fills
- Classify OTC/block executions with is_block_trade so they take the synthetic-ID path instead of failing
- Never fabricate an order_id client-side to bypass the error — it corrupts order-fill linkage
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
- AX instrument product must be non-empty without surrounding
- AX final-settlement fill must also be classified as a block
- AX fills total_count must be non-negative, was {total_count}
- AX fills total_count changed during pagination: expected {ex
- AX fills pagination returned duplicate trade ID {}
AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16).
Data as JSON: /api/errors/681273b4c88d6235.
Report an issue: GitHub.