{"record":{"id":"29985f6e2062b358","repo":"nautechsystems/nautilus_trader","slug":"cannot-extract-market-id-from-instrument-id","errorCode":null,"errorMessage":"Cannot extract market ID from {instrument_id}","messagePattern":"Cannot extract market ID from (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/betfair/src/common/parse.rs","lineNumber":507,"sourceCode":"    )\n    .with_info(info))\n}\n\n/// Extracts the Betfair market ID from a Nautilus instrument ID.\n///\n/// Instrument IDs follow the format `{market_id}-{selection_id}.BETFAIR`\n/// or `{market_id}-{selection_id}-{handicap}.BETFAIR`.\n///\n/// # Errors\n///\n/// Returns an error if the symbol does not contain a hyphen separator.\npub fn extract_market_id(instrument_id: &InstrumentId) -> anyhow::Result<String> {\n    let symbol = instrument_id.symbol.as_str();\n    let parts: Vec<&str> = symbol.splitn(3, '-').collect();\n    if parts.len() >= 2 {\n        Ok(parts[0].to_string())\n    } else {\n        anyhow::bail!(\"Cannot extract market ID from {instrument_id}\")\n    }\n}\n\n/// Extracts the selection ID and handicap from a Nautilus instrument ID.\n///\n/// # Errors\n///\n/// Returns an error if the symbol cannot be parsed into the expected format.\npub fn extract_selection_id(\n    instrument_id: &InstrumentId,\n) -> anyhow::Result<(SelectionId, Decimal)> {\n    let symbol = instrument_id.symbol.as_str();\n    let parts: Vec<&str> = symbol.splitn(3, '-').collect();\n    if parts.len() < 2 {\n        anyhow::bail!(\"Cannot extract selection ID from {instrument_id}\");\n    }\n\n    let selection_id: SelectionId = parts[1]","sourceCodeStart":489,"sourceCodeEnd":525,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/a4b06ed870971b5671d12754ea138a3ab99b1dec/crates/adapters/betfair/src/common/parse.rs#L489-L525","documentation":"Betfair instrument symbols encode both a market and a runner: {market_id}-{selection_id} or {market_id}-{selection_id}-{handicap} (e.g. 1.246503964-12345-0.5). extract_market_id splits the symbol on '-' and takes the first segment; with fewer than 2 segments there is no separator and the market id cannot be extracted, so it bails. This runs on execution paths (submit/cancel) and data/provider lookups.","triggerScenarios":"An InstrumentId built from a bare Betfair market id like 1.246503964 (no -selection suffix); passing an instrument from another venue or a hand-assembled symbol into betfair execution or data requests; stripping the selection component during config or mapping.","commonSituations":"Users confuse Betfair market ids (dot-separated, 1.xxxxx) with Nautilus instrument ids; strategies subscribe to instruments by market catalogue but orders reference a market id string; symbols mangled by config templating or string transforms that drop hyphens.","solutions":["Always construct ids via make_instrument_id(market_id, selection_id, handicap) instead of string concatenation","If you only hold a market id, it is not an instrument — resolve selections from the market catalogue first, then build the id","Validate the symbol format (>= 2 hyphen-separated parts) before sending orders or data requests","Check the instrument's venue is BETFAIR and the symbol was not truncated by config/env parsing"],"exampleFix":"// before\nlet id = InstrumentId::from(\"1.246503964.BETFAIR\"); // bare market id — no selection\n// after\nlet id = make_instrument_id(\"1.246503964\", 12345, Decimal::ZERO);","handlingStrategy":"validation","validationCode":"// Validate Betfair instrument id shape before calling execution/data APIs\nfn is_valid_betfair_instrument_id(id: &InstrumentId) -> bool {\n    if id.venue.as_str() != \"BETFAIR\" {\n        return false;\n    }\n    let parts: Vec<&str> = id.symbol.as_str().splitn(3, '-').collect();\n    parts.len() >= 2\n        && parts[0].split('.').count() == 2 // market id like 1.246503964\n        && parts[1].parse::<u64>().is_ok()\n        && (parts.len() == 2 || parts[2].parse::<Decimal>().is_ok())\n}\n\nif !is_valid_betfair_instrument_id(&instrument_id) {\n    anyhow::bail!(\"refusing order for malformed Betfair instrument {instrument_id}\");\n}","typeGuard":null,"tryCatchPattern":"match extract_market_id(&instrument_id) {\n    Ok(market_id) => submit_order(market_id, /* ... */).await,\n    Err(e) => {\n        log::error!(\"rejecting order for unparseable instrument {instrument_id}: {e}\");\n        // do not send; surface to strategy as a rejected command\n        Err(e)\n    }\n}","preventionTips":["Never build Betfair instrument ids by string concatenation — use make_instrument_id(market_id, selection_id, handicap)","Remember a Betfair market id (1.xxxxx) alone is not an instrument; it must pair with a selection id","Validate instrument ids at strategy config load time, not at order-send time"],"tags":["betfair","instrument-id","parsing","symbol-format","execution"],"backgroundTag":"instrument-id-parse-error","analyzedSha":"a4b06ed870971b5671d12754ea138a3ab99b1dec","analyzedAt":"2026-08-16T22:54:50.089Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}