nautechsystems/nautilus_trader · error · anyhow::Error
Invalid BAG contract: must have security_type=Spread and non
Error message
Invalid BAG contract: must have security_type=Spread and non-empty combo_legs
What it means
fetch_bag_contract loads a BAG (combo/spread) contract from IB and first validates the input contract. IB represents spreads as security type 'Spread' (BAG) with a non-empty combo_legs list; anything else cannot be decomposed into legs, so the call fails with this validation error.
Source
Thrown at crates/adapters/interactive_brokers/src/providers/instruments.rs:2323
/// Returns the number of spread instruments loaded (0 or 1).
///
/// # Errors
///
/// Returns an error if fetching fails.
///
/// # Notes
///
/// This method now auto-loads all leg instruments from combo_legs and creates
/// a proper spread instrument, matching Python's `_load_bag_contract` behavior.
pub async fn fetch_bag_contract(
&self,
client: &ibapi::Client,
bag_contract: &Contract,
) -> anyhow::Result<usize> {
// Validate BAG contract
if bag_contract.security_type != SecurityType::Spread || bag_contract.combo_legs.is_empty()
{
anyhow::bail!(
"Invalid BAG contract: must have security_type=Spread and non-empty combo_legs"
);
}
tracing::debug!(
"Loading BAG contract with {} legs",
bag_contract.combo_legs.len()
);
// First, load all individual leg instruments and collect their details
let mut leg_contract_details = Vec::new();
let mut leg_tuples = Vec::new();
for combo_leg in &bag_contract.combo_legs {
// Create a leg contract using information from the combo leg
let leg_contract = Contract {
contract_id: combo_leg.contract_id, // Use conId from combo_leg
symbol: bag_contract.symbol.clone(), // Use underlying symbol from BAGView on GitHub (pinned to 18893faf8b)
Solutions
- Ensure the contract has security_type = SecurityType::Spread before calling the BAG load path.
- Populate combo_legs with at least one leg (con_id, ratio, action).
- Check upstream code isn't misclassifying the contract as BAG when it is a single instrument.
- Fetch contract details from IB to populate legs automatically rather than constructing by hand.
Example fix
// before: mislabeled BAG
let bag = Contract { symbol: "SPY".into(), security_type: SecurityType::Stock, ..Default::default() };
provider.fetch_bag_contract(&client, &bag).await?;
// after: proper BAG definition
let bag = Contract {
symbol: "SPY,VND".into(),
security_type: SecurityType::Spread,
combo_legs: vec![leg_buy, leg_sell],
..Default::default()
};
provider.fetch_bag_contract(&client, &bag).await?; Defensive patterns
Strategy: validation
Validate before calling
fn is_valid_bag(contract: &Contract) -> bool {
contract.security_type == SecurityType::Spread && !contract.combo_legs.is_empty()
} Prevention
- Validate Spread + non-empty combo_legs before any BAG load call
- Build spreads programmatically from IB contract details rather than by hand
- Unit-test spread construction helpers
When it happens
Trigger: get_instrument calls fetch_bag_contract with a contract whose security_type is not SecurityType::Spread or whose combo_legs is empty.
Common situations: Passing a single-leg contract or a plain future/option to the BAG loading path, or a BAG contract built without legs (missing con_id/ratio/action data).
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
- Cannot resolve BAG contract without combo legs or cached con
- Resolved BAG spread {spread_instrument_id} is not cached
- No valid legs loaded for BAG contract
- instrument_ratios list needs to have at least 2 legs
- ratio cannot be zero
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/a8d51b588a5b7865.
Report an issue: GitHub.