{"record":{"id":"30e3738139643af5","repo":"nautechsystems/nautilus_trader","slug":"exchange-order-param-must-be-a-string","errorCode":null,"errorMessage":"`exchange` order param must be a string","messagePattern":"`exchange` order param must be a string","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/interactive_brokers/src/execution/core_tracking.rs","lineNumber":133,"sourceCode":"        instrument_provider\n            .resolve_contract_for_instrument(instrument_id)\n            .context(\"Failed to convert instrument ID to IB contract\")\n    }\n\n    pub(super) fn contract_with_order_exchange_param(\n        mut contract: ibapi::contracts::Contract,\n        params: Option<&nautilus_core::Params>,\n    ) -> anyhow::Result<ibapi::contracts::Contract> {\n        let Some(params) = params else {\n            return Ok(contract);\n        };\n\n        let Some(exchange_value) = params.get(\"exchange\") else {\n            return Ok(contract);\n        };\n\n        let Some(exchange) = exchange_value.as_str() else {\n            anyhow::bail!(\"`exchange` order param must be a string\");\n        };\n\n        if exchange.is_empty() {\n            return Ok(contract);\n        }\n\n        contract.exchange = ibapi::contracts::Exchange::from(exchange);\n        Ok(contract)\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    pub(super) fn cache_order_tracking(\n        ib_order_id: i32,\n        client_order_id: ClientOrderId,\n        instrument_id: InstrumentId,\n        trader_id: TraderId,\n        strategy_id: StrategyId,\n        order_side: OrderSide,","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/interactive_brokers/src/execution/core_tracking.rs#L115-L151","documentation":"When building an IB contract from order params, the adapter reads an optional 'exchange' entry from the params map. If the key exists but its value is not a JSON string (e.g. a number, bool, array, or object), the adapter refuses to proceed with this error instead of silently coercing or ignoring the value.","triggerScenarios":"Calling the contract-building function (contract_with_order_exchange_param) with an order params map that contains params[\"exchange\"] set to a non-string value, such as a nested object, integer, or null-like typed value.","commonSituations":"Config files or message payloads where exchange is provided as a numeric enum or parsed incorrectly (e.g. YAML/JSON typed as a number); passing an untagged enum or Option directly instead of a string; deserializing venue config into the wrong type.","solutions":["Ensure params[\"exchange\"] is a plain string, e.g. \"SMART\", \"NYSE\", \"NASDAQ\".","Fix the upstream config/deserialization so exchange is parsed as a string type, not a number or enum object.","If the value can be legitimately absent, remove the key entirely rather than passing a non-string placeholder.","Coerce known numeric venue codes to their string names before building params."],"exampleFix":"// before\nlet params = serde_json::json!({ \"exchange\": 1 });\n// after\nlet params = serde_json::json!({ \"exchange\": \"SMART\" });","handlingStrategy":"validation","validationCode":"fn validate_exchange_param(params: &serde_json::Map<String, serde_json::Value>) -> Result<(), String> {\n    match params.get(\"exchange\") {\n        Some(v) if v.as_str().is_some() => Ok(()),\n        Some(v) => Err(format!(\"exchange param must be a string, got: {v}\")),\n        None => Ok(()), // optional\n    }\n}","typeGuard":"fn is_string_param(v: &serde_json::Value) -> Option<&str> {\n    v.as_str()\n}","tryCatchPattern":null,"preventionTips":["Type venue/exchange config as String in your structs so deserialization rejects non-strings early","Use 'SMART' or the exact IB exchange names as strings","Never pass numeric venue codes; map them to IB string names first","Validate order params against a schema before building contracts"],"tags":["contract","order-params","type-mismatch","validation"],"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-14T05:17:10.506Z"}