{"record":{"id":"70b06974d690b292","repo":"nautechsystems/nautilus_trader","slug":"instrument-id-instrument-id-is-not-for-venue-70b069","errorCode":null,"errorMessage":"instrument ID `{instrument_id}` is not for venue {}","messagePattern":"instrument ID `(.+?)` is not for venue (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/derive/src/data.rs","lineNumber":2397,"sourceCode":"\nfn trade_channel(instrument: &InstrumentAny) -> anyhow::Result<String> {\n    let instrument_type = derive_instrument_type(instrument)?.to_string();\n    let instrument_id = instrument.id();\n    let currency = currency_from_instrument_id(&instrument_id)?;\n    Ok(trades_channel(&instrument_type, currency))\n}\n\nfn derive_instrument_type(instrument: &InstrumentAny) -> anyhow::Result<DeriveInstrumentType> {\n    match instrument {\n        InstrumentAny::CryptoPerpetual(_) => Ok(DeriveInstrumentType::Perp),\n        InstrumentAny::CryptoOption(_) => Ok(DeriveInstrumentType::Option),\n        InstrumentAny::CurrencyPair(_) => Ok(DeriveInstrumentType::Erc20),\n        other => anyhow::bail!(\"unsupported Derive instrument type for trades: {other:?}\"),\n    }\n}\n\nfn currency_from_instrument_id(instrument_id: &InstrumentId) -> anyhow::Result<&str> {\n    anyhow::ensure!(\n        instrument_id.venue == *DERIVE_VENUE,\n        \"instrument ID `{instrument_id}` is not for venue {}\",\n        DERIVE_VENUE.as_str(),\n    );\n\n    instrument_id\n        .symbol\n        .as_str()\n        .split_once('-')\n        .and_then(|(currency, _)| (!currency.is_empty()).then_some(currency))\n        .ok_or_else(|| anyhow::anyhow!(\"cannot derive currency from {instrument_id}\"))\n}\n\n// Caps the rendered JSON at ~512 bytes for log grep-ability and backs the\n// slice off to a UTF-8 char boundary so a multi-byte codepoint near the cap\n// can never produce a panicking slice.\nfn truncated_payload_snippet(raw: &str) -> String {\n    const MAX_LEN: usize = 512;","sourceCodeStart":2379,"sourceCodeEnd":2415,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/derive/src/data.rs#L2379-L2415","documentation":"Derive adapter's currency_from_instrument_id helper only accepts instrument IDs belonging to the Derive venue. If the caller passes an instrument ID minted for a different venue (or with a malformed venue portion), the ensure! macro aborts with this message naming the offending ID and the expected venue. It is a guard against silently deriving a currency from a non-Derive instrument.","triggerScenarios":"Calling Derive data/execution code paths that resolve a currency (e.g. to build request parameters) with an InstrumentId whose venue != Derive, such as passing a Binance instrument ID into a Derive API call, or constructing an InstrumentId manually with a typo'd venue string.","commonSituations":"Routing logic that forwards instrument IDs from a multi-venue portfolio into Derive-specific helpers; copy-pasted instrument IDs in config or test fixtures; venue string casing mistakes when parsing user input.","solutions":["Ensure the InstrumentId passed to Derive calls was created for the Derive venue (use the adapter's DERIVE_VENUE constant or its instrument-ID parsing helper).","Verify the venue portion of the ID string matches Derive exactly (case/format as defined by DERIVE_VENUE).","If handling multiple venues, filter or dispatch instrument IDs by venue before reaching the Derive client.","Log the full instrument ID and expected venue to find where the cross-venue ID leaked in."],"exampleFix":"// before\nlet id = InstrumentId::from(\"ETH-PERP.BINANCE\");\nderive_client.currency_from_instrument_id(&id)?;\n// after\nlet id = InstrumentId::from(\"ETH-PERP.DERIVE\");\nderive_client.currency_from_instrument_id(&id)?;","handlingStrategy":"validation","validationCode":"fn ensure_derive_instrument(id: &InstrumentId) -> Result<(), String> {\n    if id.venue.as_str() != DERIVE_VENUE.as_str() {\n        return Err(format!(\"{} is not a Derive instrument (venue {})\", id, id.venue));\n    }\n    Ok(())\n}","typeGuard":"fn is_derive_instrument(id: &InstrumentId) -> bool {\n    id.venue == *DERIVE_VENUE\n}","tryCatchPattern":"match derive_client.currency_from_instrument_id(&id) {\n    Ok(cur) => /* use currency */,\n    Err(e) => log::warn!(\"skip non-Derive instrument: {e}\"),\n}","preventionTips":["Only route Derive-venue instrument IDs into Derive client calls","Dispatch by venue before calling adapter-specific helpers","Derive instrument IDs from the adapter's instrument provider, not hand-built strings"],"tags":["derive","instrument-id","venue-validation","rust"],"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-14T00:17:10.932Z"}