nautechsystems/nautilus_trader · error · anyhow::Error
`exchange` order param must be a string
Error message
`exchange` order param must be a string
What it means
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.
Source
Thrown at crates/adapters/interactive_brokers/src/execution/core_tracking.rs:133
instrument_provider
.resolve_contract_for_instrument(instrument_id)
.context("Failed to convert instrument ID to IB contract")
}
pub(super) fn contract_with_order_exchange_param(
mut contract: ibapi::contracts::Contract,
params: Option<&nautilus_core::Params>,
) -> anyhow::Result<ibapi::contracts::Contract> {
let Some(params) = params else {
return Ok(contract);
};
let Some(exchange_value) = params.get("exchange") else {
return Ok(contract);
};
let Some(exchange) = exchange_value.as_str() else {
anyhow::bail!("`exchange` order param must be a string");
};
if exchange.is_empty() {
return Ok(contract);
}
contract.exchange = ibapi::contracts::Exchange::from(exchange);
Ok(contract)
}
#[allow(clippy::too_many_arguments)]
pub(super) fn cache_order_tracking(
ib_order_id: i32,
client_order_id: ClientOrderId,
instrument_id: InstrumentId,
trader_id: TraderId,
strategy_id: StrategyId,
order_side: OrderSide,View on GitHub (pinned to 18893faf8b)
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.
Example fix
// before
let params = serde_json::json!({ "exchange": 1 });
// after
let params = serde_json::json!({ "exchange": "SMART" }); Defensive patterns
Strategy: validation
Validate before calling
fn validate_exchange_param(params: &serde_json::Map<String, serde_json::Value>) -> Result<(), String> {
match params.get("exchange") {
Some(v) if v.as_str().is_some() => Ok(()),
Some(v) => Err(format!("exchange param must be a string, got: {v}")),
None => Ok(()), // optional
}
} Type guard
fn is_string_param(v: &serde_json::Value) -> Option<&str> {
v.as_str()
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Conditional orders require a trigger price
- GTD time in force requires expire_time parameter
- canonical run field '{field}' must be a decimal string
- Unsupported `OrderSide` for Binance: {value:?}
- invalid OrderSide: must be Buy or Sell, was {side}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/30e3738139643af5.
Report an issue: GitHub.