{"record":{"id":"e4c66a7f5b0f42ce","repo":"nautechsystems/nautilus_trader","slug":"all-venues-must-match-expected-was","errorCode":null,"errorMessage":"All venues must match. Expected {}, was {}","messagePattern":"All venues must match\\. Expected (.+?), was (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/interactive_brokers/src/common/parse.rs","lineNumber":1170,"sourceCode":"/// - Less than 2 legs provided\n/// - Any ratio is zero\n/// - Venues don't match across legs\npub fn create_spread_instrument_id(\n    leg_tuples: &[(InstrumentId, i32)],\n) -> anyhow::Result<InstrumentId> {\n    if leg_tuples.len() < 2 {\n        anyhow::bail!(\"instrument_ratios list needs to have at least 2 legs\");\n    }\n\n    let first_venue = leg_tuples[0].0.venue;\n\n    for (instrument_id, ratio) in leg_tuples {\n        if *ratio == 0 {\n            anyhow::bail!(\"ratio cannot be zero\");\n        }\n\n        if instrument_id.venue != first_venue {\n            anyhow::bail!(\n                \"All venues must match. Expected {}, was {}\",\n                first_venue,\n                instrument_id.venue\n            );\n        }\n    }\n\n    let mut sorted_ratios = leg_tuples.to_vec();\n    sorted_ratios.sort_by(|a, b| a.0.symbol.as_str().cmp(b.0.symbol.as_str()));\n\n    let symbol_parts = sorted_ratios\n        .iter()\n        .map(|(instrument_id, ratio)| {\n            if *ratio > 0 {\n                format!(\"({}){}\", ratio, instrument_id.symbol.as_str())\n            } else {\n                format!(\"(({})){}\", ratio.abs(), instrument_id.symbol.as_str())\n            }","sourceCodeStart":1152,"sourceCodeEnd":1188,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/interactive_brokers/src/common/parse.rs#L1152-L1188","documentation":"All legs of a spread must belong to the same venue because the resulting spread InstrumentId encodes a single venue; mixed venues cannot be represented. The error names the expected (first leg's) venue and the mismatching leg's venue.","triggerScenarios":"Calling create_spread_instrument_id with legs from different venues, e.g. mixing '.OPRA' option legs with another venue's instruments, or leg InstrumentIds parsed with inconsistent/incorrect venue suffixes.","commonSituations":"Combining option legs from different data sources, misconfigured venue mappings during parsing, or spreading across venues in multi-venue setups.","solutions":["Ensure every leg InstrumentId uses the same venue suffix before building the spread","Fix the venue assignment when constructing leg InstrumentIds (e.g. wrong exchange mapping)","Filter out legs from other venues instead of mixing them"],"exampleFix":"// before\nlet legs = vec![(InstrumentId::from(\"AAPL  251212C00250000.OPRA\"), -1),\n                (InstrumentId::from(\"MSFT  251212P00400000.OPRA\"), 1)]; // venue must match, MSFT leg wrong here\n// after\nlet legs = vec![(InstrumentId::from(\"AAPL  251212C00250000.OPRA\"), -1),\n                (InstrumentId::from(\"AAPL  251212P00250000.OPRA\"), 1)];","handlingStrategy":"validation","validationCode":"let first = legs.first().map(|(id, _)| id.venue);\nif legs.iter().any(|(id, _)| Some(id.venue) != first) {\n    return Err(anyhow::anyhow!(\"spread legs span multiple venues\"));\n}","typeGuard":null,"tryCatchPattern":"match create_spread_instrument_id(&legs) {\n    Ok(id) => id,\n    Err(e) if e.to_string().contains(\"All venues must match\") => {\n        tracing::error!(\"mixed-venue legs: {e}\");\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Normalize all leg InstrumentIds to the same venue before building spreads","Verify venue/exchange mappings when parsing leg contracts","Keep multi-venue instruments out of single combo instruments"],"tags":["rust","options-spreads","venues","interactive-brokers"],"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"}