nautechsystems/nautilus_trader · error

Cannot extract selection ID from {instrument_id}

Error message

Cannot extract selection ID from {instrument_id}

What it means

extract_selection_id splits the instrument symbol on '-' (max 3 parts) and parses part 2 as the SelectionId (u64) and, if present, part 3 as the handicap Decimal. With fewer than 2 segments there is no selection component and it bails before parsing. Malformed numeric parts fail later with separate 'invalid selection ID' / 'invalid handicap' messages.

Source

Thrown at crates/adapters/betfair/src/common/parse.rs:522

    if parts.len() >= 2 {
        Ok(parts[0].to_string())
    } else {
        anyhow::bail!("Cannot extract market ID from {instrument_id}")
    }
}

/// Extracts the selection ID and handicap from a Nautilus instrument ID.
///
/// # Errors
///
/// Returns an error if the symbol cannot be parsed into the expected format.
pub fn extract_selection_id(
    instrument_id: &InstrumentId,
) -> anyhow::Result<(SelectionId, Decimal)> {
    let symbol = instrument_id.symbol.as_str();
    let parts: Vec<&str> = symbol.splitn(3, '-').collect();
    if parts.len() < 2 {
        anyhow::bail!("Cannot extract selection ID from {instrument_id}");
    }

    let selection_id: SelectionId = parts[1]
        .parse()
        .with_context(|| format!("invalid selection ID in {instrument_id}"))?;

    let handicap = if parts.len() == 3 {
        parts[2]
            .parse::<Decimal>()
            .with_context(|| format!("invalid handicap in {instrument_id}"))?
    } else {
        Decimal::ZERO
    };

    Ok((selection_id, handicap))
}

#[cfg(test)]

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Build ids with make_instrument_id so market id, selection id, and handicap are always present
  2. Pre-validate the symbol has at least 2 hyphen-separated parts and the second parses as u64 before calling execution APIs
  3. Verify the instrument definition being traded actually came from the Betfair adapter (venue check)
  4. Fix the upstream mapping that produced the truncated or foreign symbol

Example fix

// before
let id = InstrumentId::from("1.246503964.BETFAIR");
let (sel, hcp) = extract_selection_id(&id)?; // fails: no '-selection' part
// after
let id = make_instrument_id("1.246503964", 12345, Decimal::ZERO);
let (sel, hcp) = extract_selection_id(&id)?;
Defensive patterns

Strategy: validation

Validate before calling

// Validate symbol shape before calling APIs that need selection extraction
fn symbol_has_selection(id: &InstrumentId) -> bool {
    let parts: Vec<&str> = id.symbol.as_str().splitn(3, '-').collect();
    parts.len() >= 2 && parts[1].parse::<u64>().is_ok()
}

if !symbol_has_selection(&instrument_id) {
    anyhow::bail!("instrument {instrument_id} lacks a '-selection' component");
}

Try / catch

match extract_selection_id(&instrument_id) {
    Ok((selection_id, handicap)) => submit_order(market_id, selection_id, handicap).await,
    Err(e) => {
        log::error!("cannot extract selection from {instrument_id}: {e}");
        // fail the command explicitly so the strategy can drop or re-route the order
        Err(e)
    }
}

Prevention

When it happens

Trigger: An InstrumentId whose symbol lacks a hyphen separator — bare market id (1.246503964), a foreign venue symbol routed to Betfair, or a hand-built string like "mymarket" — reaches order submission (submit/cancel paths call extract_selection_id after extract_market_id).

Common situations: Replaying recorded instruments through a different venue config; config files or env vars truncating the symbol at the hyphen; integrations that map their own selection keys directly into symbols without the market-id prefix.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16). Data as JSON: /api/errors/4bb23f2039096820. Report an issue: GitHub.