{"record":{"id":"1958953e6aab7403","repo":"nautechsystems/nautilus_trader","slug":"fill-missing-both-trd-match-id-and-exec-id","errorCode":null,"errorMessage":"Fill missing both trd_match_id and exec_id","messagePattern":"Fill missing both trd_match_id and exec_id","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/bitmex/src/http/parse.rs","lineNumber":1104,"sourceCode":"    // Skip non-trade executions (funding, settlements, etc.)\n    // Trade executions have exec_type of Trade and must have order_id\n    if !matches!(exec.exec_type, BitmexExecType::Trade) {\n        anyhow::bail!(\"Skipping non-trade execution: {:?}\", exec.exec_type);\n    }\n\n    // Additional check: skip executions without order_id (likely funding/settlement)\n    let order_id = exec.order_id.ok_or_else(|| {\n        anyhow::anyhow!(\"Skipping execution without order_id: {:?}\", exec.exec_type)\n    })?;\n\n    let account_id = bitmex_account_id(exec.account);\n    let instrument_id = instrument.id();\n    let venue_order_id = VenueOrderId::new(order_id.to_string());\n    // trd_match_id might be missing for some execution types, use exec_id as fallback\n    let trade_id = TradeId::new(\n        exec.trd_match_id\n            .or(Some(exec.exec_id))\n            .ok_or_else(|| anyhow::anyhow!(\"Fill missing both trd_match_id and exec_id\"))?\n            .to_string(),\n    );\n    // Skip executions without side (likely not trades)\n    let Some(side) = exec.side else {\n        anyhow::bail!(\"Skipping execution without side: {:?}\", exec.exec_type);\n    };\n    let order_side = OrderSide::from(side);\n    let last_qty = parse_signed_contracts_quantity(exec.last_qty, instrument);\n    let last_px = Price::new(exec.last_px, instrument.price_precision());\n\n    // Map BitMEX currency to standard currency code\n    let settlement_currency_str = exec.settl_currency.unwrap_or(Ustr::from(\"XBT\")).as_str();\n    let mapped_currency = map_bitmex_currency(settlement_currency_str);\n    let currency = get_currency(&mapped_currency);\n    let commission = Money::new(exec.commission.unwrap_or(0.0), currency);\n    let liquidity_side = parse_liquidity_side(&exec.last_liquidity_ind);\n    let client_order_id = exec.cl_ord_id.map(ClientOrderId::new);\n    let venue_position_id = None; // Not applicable on BitMEX","sourceCodeStart":1086,"sourceCodeEnd":1122,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/bitmex/src/http/parse.rs#L1086-L1122","documentation":"parse_fill_report builds a TradeId from `trd_match_id`, falling back to `exec_id`. This error fires only when BOTH fields are absent, meaning the fill cannot be uniquely identified. The parser fails so the record is not silently mis-attributed.","triggerScenarios":"request_fill_reports encountering an execution row where both trd_match_id and exec_id are null — malformed or highly unusual BitMEX execution payloads.","commonSituations":"Unusual execution types in fill history (transfers, adjustments); BitMEX API schema drift where a previously-guaranteed field becomes optional; corrupted/partial REST responses.","solutions":["Catch and skip the record in the caller, logging the raw execution for diagnosis.","Verify the BitMEX API response shape for the affected executions (dump raw JSON).","Update the adapter if BitMEX changed field naming/optionality.","Filter executions to those with an exec_id before parsing."],"exampleFix":"// before\nlet trade_id = TradeId::new(exec.trd_match_id.or(Some(exec.exec_id)).ok_or_else(|| anyhow::anyhow!(\"Fill missing both trd_match_id and exec_id\"))?.to_string());\n// after\nlet Some(id_src) = exec.trd_match_id.or(Some(exec.exec_id)) else {\n    log::warn!(\"Skipping fill without trd_match_id/exec_id\");\n    return Ok(None);\n};\nlet trade_id = TradeId::new(id_src.to_string());","handlingStrategy":"type-guard","validationCode":"if exec.trd_match_id.is_none() && exec.exec_id.is_empty() { /* skip record */ }","typeGuard":"fn has_trade_id(exec: &BitmexExecution) -> bool {\n    exec.trd_match_id.is_some() || !exec.exec_id.is_empty()\n}","tryCatchPattern":"let Ok(report) = parse_fill_report(exec, instrument) else {\n    log::debug!(\"fill without trade id skipped\");\n    continue;\n};","preventionTips":["Verify exec_id is always populated in your BitMEX response versions.","Dump raw execution JSON when this fires to detect API schema drift.","Skip rather than fail when a fill cannot be uniquely identified."],"tags":["bitmex","fills","missing-data","trade-id"],"backgroundTag":"missing-required-argument","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}