{"record":{"id":"ba655b1fdbc892aa","repo":"nautechsystems/nautilus_trader","slug":"failed-to-convert-filled-qty-to-decimal-filled","errorCode":null,"errorMessage":"Failed to convert filled qty to Decimal: {filled}","messagePattern":"Failed to convert filled qty to Decimal: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/interactive_brokers/src/execution/core_updates.rs","lineNumber":933,"sourceCode":"        order_fill_progress: &Arc<Mutex<AHashMap<ClientOrderId, (Decimal, Decimal)>>>,\n    ) -> anyhow::Result<()> {\n        let is_spread_order = is_spread_instrument_id(instrument_id);\n        if filled <= 0.0 || !parse::should_use_avg_fill_price(avg_fill_price, instrument_id) {\n            return Ok(());\n        }\n\n        let Some(instrument) = instrument_provider.find(instrument_id) else {\n            return Ok(());\n        };\n\n        let price_magnifier = instrument_provider.get_price_magnifier(instrument_id) as f64;\n        let converted_avg_price = avg_fill_price * price_magnifier;\n        let avg_px = Price::new(converted_avg_price, instrument.price_precision());\n\n        order_avg_prices.lock().insert(client_order_id, avg_px);\n\n        let filled_decimal = Decimal::from_f64_retain(filled)\n            .ok_or_else(|| anyhow::anyhow!(\"Failed to convert filled qty to Decimal: {filled}\"))?;\n        let avg_decimal = Decimal::from_f64_retain(converted_avg_price).ok_or_else(|| {\n            anyhow::anyhow!(\"Failed to convert avg fill price to Decimal: {converted_avg_price}\")\n        })?;\n\n        let mut progress = order_fill_progress.lock();\n        let (previous_filled, previous_notional) = progress\n            .get(&client_order_id)\n            .copied()\n            .unwrap_or((Decimal::ZERO, Decimal::ZERO));\n        let total_notional = filled_decimal * avg_decimal;\n        progress.insert(client_order_id, (filled_decimal, total_notional));\n        drop(progress);\n\n        let fill_delta = filled_decimal - previous_filled;\n        if fill_delta <= Decimal::ZERO || !is_spread_order {\n            return Ok(());\n        }\n","sourceCodeStart":915,"sourceCodeEnd":951,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/interactive_brokers/src/execution/core_updates.rs#L915-L951","documentation":"The adapter converts the IB-reported filled quantity (an f64) into a Decimal using from_f64_retain, which returns None for NaN, infinite, or otherwise non-representable values. When the filled quantity from an IB execution report is not a finite finite number, this error is thrown so no bogus fill progress is recorded.","triggerScenarios":"update_order_avg_price is called with filled = NaN or +/-inf (from ibapi::OrderData fills or division in the combo-fill path), so Decimal::from_f64_retain(filled) returns None.","commonSituations":"Malformed or placeholder values in IB execution/commission reports; downstream arithmetic producing inf (e.g. division by zero fill delta) passed back into this function.","solutions":["Validate that the filled quantity is finite before calling update_order_avg_price, and skip/quote the update otherwise.","Check the IB execution report data for NaN/inf fields; upgrade or fix the ibapi parsing if it emits sentinel values.","Log the raw execution data when this occurs to identify the upstream message producing the bad quantity."],"exampleFix":"// before\nlet filled_decimal = Decimal::from_f64_retain(filled)\n    .ok_or_else(|| anyhow::anyhow!(\"Failed to convert filled qty to Decimal: {filled}\"))?;\n// after\nif !filled.is_finite() || filled < 0.0 {\n    tracing::warn!(\"Skipping avg price update with invalid filled qty: {filled}\");\n    return Ok(());\n}\nlet filled_decimal = Decimal::from_f64_retain(filled)\n    .ok_or_else(|| anyhow::anyhow!(\"Failed to convert filled qty to Decimal: {filled}\"))?;","handlingStrategy":"validation","validationCode":"// Rust: validate the IB-reported fill quantity before conversion\nfn valid_filled_qty(filled: f64) -> bool {\n    filled.is_finite() && filled >= 0.0\n}","typeGuard":"fn is_finite_positive(x: f64) -> bool {\n    x.is_finite() && x > 0.0\n}","tryCatchPattern":"let filled_decimal = match Decimal::from_f64_retain(filled) {\n    Some(d) => d,\n    None => {\n        tracing::error!(\"Invalid filled qty {filled}; skipping avg price update\");\n        return Ok(());\n    }\n};","preventionTips":["Always check f64.is_finite() on quantities and prices coming from venue callbacks","Log raw IB execution reports to catch sentinel NaN values early","Pin and test against the ibapi version you deploy so parsing changes are caught"],"tags":["decimal","conversion","nan","fill-data","rust"],"backgroundTag":"invalid-argument-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}