{"record":{"id":"637b9dd5c2455222","repo":"nautechsystems/nautilus_trader","slug":"leg-contract-details-must-be-provided","errorCode":null,"errorMessage":"leg_contract_details must be provided","messagePattern":"leg_contract_details must be provided","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/interactive_brokers/src/providers/parse.rs","lineNumber":674,"sourceCode":"\n    InstrumentAny::from(instrument)\n}\n\n/// Parse a spread instrument ID into an OptionSpread instrument.\n///\n/// This implements the same logic as Python's `parse_spread_instrument_id`.\n/// Uses contract details from the first leg to determine spread properties.\n///\n/// # Errors\n///\n/// Returns an error if parsing fails.\npub fn parse_spread_instrument_id(\n    instrument_id: InstrumentId,\n    leg_contract_details: &[(&ibapi::contracts::ContractDetails, i32)],\n    timestamp_ns: Option<UnixNanos>,\n) -> anyhow::Result<OptionSpread> {\n    if leg_contract_details.is_empty() {\n        anyhow::bail!(\"leg_contract_details must be provided\");\n    }\n\n    // Use contract details from first leg\n    let (first_details, _) = leg_contract_details[0];\n    let first_contract = &first_details.contract;\n\n    // Extract properties from the first leg contract details\n    let currency = Currency::from(first_contract.currency.to_string());\n    let underlying = if !first_details.under_symbol.is_empty() {\n        Ustr::from(first_details.under_symbol.as_str())\n    } else {\n        Ustr::from(first_contract.symbol.as_str())\n    };\n\n    // Parse multiplier\n    let multiplier_str = first_contract.multiplier.to_string();\n    let multiplier =\n        Quantity::from_str(&multiplier_str).unwrap_or_else(|_| Quantity::new(100.0, 0)); // Default to 100 for options","sourceCodeStart":656,"sourceCodeEnd":692,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/interactive_brokers/src/providers/parse.rs#L656-L692","documentation":"parse_spread_instrument_id builds an OptionSpread from IBKR contract details for each leg. It requires a non-empty leg_contract_details slice because it reads the first leg's contract details to derive the spread's metadata. An empty slice is a caller programming error, so it bails immediately before any parsing.","triggerScenarios":"Calling parse_spread_instrument_id (directly or via parse_option_spread_instrument_id) with an empty slice of (&ContractDetails, i32) leg tuples — e.g. when the IBKR contract-details request returned nothing for all legs or the caller built the leg list without pushing any entries.","commonSituations":"Fetching option chain contract details returned zero matches (wrong expiry/strike filters), a bug in collecting leg details before calling the parser, or passing a default-initialized/filtered-out leg list.","solutions":["Ensure each spread leg's ContractDetails is fetched successfully from the IBKR client before calling the parser; retry or log failed leg requests.","Validate leg_contract_details is non-empty at the call site and surface a domain error identifying the missing instrument instead of reaching the parser.","If parse_option_spread_instrument_id produced empty legs, verify the instrument_id parses to a valid option spread symbol and that the contract-details lookups use the correct exchange/currency."],"exampleFix":"// before\nlet legs: Vec<(&ContractDetails, i32)> = Vec::new();\nlet spread = parse_spread_instrument_id(instrument_id, &legs, ts)?;\n// after\nif legs.is_empty() {\n    anyhow::bail!(\"no contract details resolved for {instrument_id}\");\n}\nlet spread = parse_spread_instrument_id(instrument_id, &legs, ts)?;","handlingStrategy":"validation","validationCode":"if leg_contract_details.is_empty() {\n    return Err(anyhow::anyhow!(\"cannot parse spread {instrument_id}: no leg contract details\"));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always await/log every leg's reqContractDetails result before parsing; fail fast per leg.","Assert non-empty legs in unit tests for spread parsing helpers.","Validate spread instrument_id parses into the expected number of legs before fetching details."],"tags":["rust","empty-argument","instrument-parsing","interactive-brokers"],"backgroundTag":"empty-required-field","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"}