nautechsystems/nautilus_trader · error
cannot derive currency from {instrument_id}
Error message
cannot derive currency from {instrument_id} What it means
currency_from_instrument_id derives the base currency by splitting the symbol on '-' and taking the first segment. If there is no '-' separator, or the segment before the dash is empty, it cannot determine the currency and bails with this message. It protects downstream Derive API calls from receiving a blank/unknown currency.
Source
Thrown at crates/adapters/derive/src/data.rs:2408
InstrumentAny::CryptoOption(_) => Ok(DeriveInstrumentType::Option),
InstrumentAny::CurrencyPair(_) => Ok(DeriveInstrumentType::Erc20),
other => anyhow::bail!("unsupported Derive instrument type for trades: {other:?}"),
}
}
fn currency_from_instrument_id(instrument_id: &InstrumentId) -> anyhow::Result<&str> {
anyhow::ensure!(
instrument_id.venue == *DERIVE_VENUE,
"instrument ID `{instrument_id}` is not for venue {}",
DERIVE_VENUE.as_str(),
);
instrument_id
.symbol
.as_str()
.split_once('-')
.and_then(|(currency, _)| (!currency.is_empty()).then_some(currency))
.ok_or_else(|| anyhow::anyhow!("cannot derive currency from {instrument_id}"))
}
// Caps the rendered JSON at ~512 bytes for log grep-ability and backs the
// slice off to a UTF-8 char boundary so a multi-byte codepoint near the cap
// can never produce a panicking slice.
fn truncated_payload_snippet(raw: &str) -> String {
const MAX_LEN: usize = 512;
if raw.len() <= MAX_LEN {
return raw.to_string();
}
let mut end = MAX_LEN;
while end > 0 && !raw.is_char_boundary(end) {
end -= 1;
}
format!("{}...(truncated)", &raw[..end])
}
#[cfg(test)]View on GitHub (pinned to 18893faf8b)
Solutions
- Use Derive instrument IDs in the BASE-QUOTE.VENUE form (e.g. "ETH-USDC.DERIVE") so split_once('-') succeeds.
- Load instruments via the Derive adapter's instrument provider so symbols follow its canonical format.
- Validate the symbol contains a non-empty '-'-separated currency before calling the API.
- Inspect the failing instrument ID in the message and correct its naming in config/code.
Example fix
// before
let id = InstrumentId::from("ETHPERP.DERIVE"); // no '-' -> cannot derive currency
// after
let id = InstrumentId::from("ETH-USDC.DERIVE"); Defensive patterns
Strategy: validation
Validate before calling
fn symbol_has_base(id: &InstrumentId) -> bool {
id.symbol.as_str().split_once('-')
.map_or(false, |(c, _)| !c.is_empty())
} Type guard
fn parse_base_currency(id: &InstrumentId) -> Option<&str> {
id.symbol.as_str().split_once('-')
.and_then(|(c, _)| (!c.is_empty()).then_some(c))
} Try / catch
let currency = derive_client.currency_from_instrument_id(&id)
.map_err(|e| { log::error!("bad Derive symbol {}: {e}", id); e })?; Prevention
- Use BASE-QUOTE.VENUE instrument naming for Derive instruments
- Load instruments via the Derive instrument provider so symbols are canonical
- Validate symbol format at config-load time rather than at call time
When it happens
Trigger: Passing an InstrumentId whose symbol has no '-' delimiter (e.g. "ETHPERP.DERIVE") or whose symbol starts with '-' (empty first segment) into any Derive code path that needs the base currency.
Common situations: Custom or synthetically constructed instrument symbols that don't follow Nautilus BASE-QUOTE convention; forward-fill or option symbols that use a different naming scheme; hand-typed instrument names in config.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- {FAILED}: {e}
- Cannot expand parent subscription for {instrument_id}: symbo
- instrument ID `{instrument_id}` is not for venue {}
- Invalid `InstrumentId` for 'instrument_id'
- Invalid `external_order_claims` instrument ID {claim}: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/0193754764e0a493.
Report an issue: GitHub.