{"record":{"id":"681273b4c88d6235","repo":"nautechsystems/nautilus_trader","slug":"ax-regular-fill-has-an-empty-order-id","errorCode":null,"errorMessage":"AX regular fill has an empty order_id","messagePattern":"AX regular fill has an empty order_id","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/architect_ax/src/http/parse.rs","lineNumber":673,"sourceCode":"\n    let trade_id = TradeId::new_checked(&fill.trade_id).context(\"Invalid trade_id in Ax fill\")?;\n    let is_block_trade = fill.is_block_trade;\n    let is_final_settlement = fill.is_final_settlement;\n    anyhow::ensure!(\n        !(is_final_settlement == Some(true) && is_block_trade == Some(false)),\n        \"AX final-settlement fill must also be classified as a block trade\"\n    );\n\n    let is_special_fill = is_block_trade == Some(true) || is_final_settlement == Some(true);\n    let venue_order_id = if is_special_fill {\n        VenueOrderId::new_checked(format!(\"AX-FILL-{}\", fill.trade_id))\n            .context(\"Invalid synthetic venue order ID for AX fill\")?\n    } else {\n        let order_id = fill\n            .order_id\n            .as_deref()\n            .context(\"AX fill is missing order_id and explicit special-fill classification\")?;\n        anyhow::ensure!(\n            !order_id.is_empty(),\n            \"AX regular fill has an empty order_id\"\n        );\n        VenueOrderId::new_checked(order_id).context(\"Invalid order_id in AX fill\")?\n    };\n\n    // Use explicit side field from fill\n    let order_side: OrderSide = fill.side.into();\n\n    let last_px = decimal_to_price_dp(fill.price, instrument.price_precision(), \"fill.price\")?;\n    let last_qty = Quantity::new(fill.quantity as f64, instrument.size_precision());\n\n    let currency = Currency::USD();\n    let commission = Money::from_decimal(fill.fee, currency)\n        .context(\"Failed to convert fill.fee Decimal to Money\")?;\n\n    let liquidity_side = if fill.is_taker {\n        LiquiditySide::Taker","sourceCodeStart":655,"sourceCodeEnd":691,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/a4b06ed870971b5671d12754ea138a3ab99b1dec/crates/adapters/architect_ax/src/http/parse.rs#L655-L691","documentation":"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}\".","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (AX fill payload)\n{\"trade_id\": \"T2\", \"order_id\": \"\", \"is_block_trade\": null, \"is_final_settlement\": null}\n// after\n{\"trade_id\": \"T2\", \"order_id\": \"OID-123\", \"is_block_trade\": null, \"is_final_settlement\": null}","handlingStrategy":"validation","validationCode":"// Pre-check linkage before parsing (parse_fill_report is pub)\nfn fill_linkage_ok(fill: &AxFill) -> bool {\n    let special = fill.is_block_trade == Some(true) || fill.is_final_settlement == Some(true);\n    special || fill.order_id.as_deref().map(|s| !s.is_empty()).unwrap_or(false)\n}\n\nfor fill in &fills {\n    if !fill_linkage_ok(fill) {\n        log::error!(\"fill {} lacks order linkage and special classification\", fill.trade_id);\n        continue;\n    }\n    reports.push(parse_fill_report(fill, account_id, &instrument, ts_init)?);\n}","typeGuard":null,"tryCatchPattern":"match parse_fill_report(fill, account_id, &instrument, ts_init) {\n    Ok(report) => reports.push(report),\n    Err(e) if e.to_string().contains(\"empty order_id\") => {\n        log::error!(\"quarantining regular fill {} without order_id: {e}\", fill.trade_id);\n        quarantined.push(fill.trade_id.clone()); // needs venue-side fix before re-import\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["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"],"tags":["architect-ax","fills","order-id","validation","parsing"],"backgroundTag":"invalid-fill-data","analyzedSha":"a4b06ed870971b5671d12754ea138a3ab99b1dec","analyzedAt":"2026-08-16T22:54:50.089Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}