nautechsystems/nautilus_trader · error
{FAILED}: {e}
Error message
{FAILED}: {e} What it means
The blanket `From<T: AsRef<str>> for InstrumentId` conversion panics when the string cannot be parsed as a valid `InstrumentId` (it must contain a symbol and venue separated by '.', e.g. "AAPL.NASDAQ", with valid non-empty parts). The panic reuses the underlying parse error message, prefixed by the FAILED constant. It exists so ergonomic `value.into()` conversions fail loudly instead of producing invalid IDs.
Source
Thrown at crates/model/src/identifiers/instrument_id.rs:209
})?
}
#[cfg(not(feature = "defi"))]
Symbol::new_checked(symbol_part).map_err(|source| InstrumentIdError::InvalidSymbol {
value: s.to_string(),
source: Box::new(source),
})?
};
Ok(Self { symbol, venue })
}
}
impl<T: AsRef<str>> From<T> for InstrumentId {
fn from(value: T) -> Self {
match Self::from_str(value.as_ref()) {
Ok(instrument_id) => instrument_id,
Err(e) => panic!("{FAILED}: {e}"),
}
}
}
impl Debug for InstrumentId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "\"{}.{}\"", self.symbol, self.venue)
}
}
impl Display for InstrumentId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}", self.symbol, self.venue)
}
}
impl Serialize for InstrumentId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure the string has the '<SYMBOL>.<VENUE>' format with non-empty symbol and venue before converting.
- Use `InstrumentId::from_str(...)` / `InstrumentId::checked_from_str` and handle the Err explicitly instead of the panicking `From` impl.
- Validate the venue against `Venue::from_str` and the symbol against `Symbol` first.
- Fix upstream data/config sources to always include the venue suffix.
Example fix
// before
let id: InstrumentId = "BTCUSDT".into(); // panics: no venue
// after
let id = InstrumentId::from_str("BTCUSDT.BINANCE").expect("valid instrument id"); Defensive patterns
Strategy: validation
Validate before calling
fn parse_instrument_id(s: &str) -> Result<InstrumentId, String> {
match s.rsplit_once('.') {
Some((sym, venue)) if !sym.is_empty() && !venue.is_empty() => {
InstrumentId::from_str(s).map_err(|e| e.to_string())
}
_ => Err(format!("expected '<SYMBOL>.<VENUE>', got '{s}'")),
}
} Type guard
fn is_instrument_id_shape(s: &str) -> bool {
matches!(s.rsplit_once('.'), Some((sym, venue)) if !sym.is_empty() && !venue.is_empty())
} Try / catch
// Prefer checked parsing over the panicking From impl
let id = match InstrumentId::from_str(raw) {
Ok(id) => id,
Err(e) => { log::error!("bad instrument id '{raw}': {e}"); return Err(e.into()); }
}; Prevention
- Always store instrument ids in SYMBOL.VENUE form in configs and data files
- Use InstrumentId::from_str/checked_from_str at boundaries and reserve From for internal trusted strings
- Validate venues against known venues at startup
- Reject empty CSV/config cells before conversion
When it happens
Trigger: Calling `InstrumentId::from("BTCUSD")` (no venue), `InstrumentId::from(".NSE")` (empty symbol), `InstrumentId::from("AAPL.nasdaq.")` (malformed venue), or `.into()` on any user-provided string with wrong format.
Common situations: Reading instrument symbols from CSV/config files where venue suffix is missing; user config like 'instrument=BTCUSDT' without exchange; joining symbol+venue with the wrong separator (':' or '-'); empty cells in data files.
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
- Invalid `InstrumentId` for 'instrument_id'
- Invalid scientific notation exponent '{exponent}': must be a
- {FAILED}: {e}
- Cannot expand parent subscription for {instrument_id}: symbo
- cannot derive currency from {instrument_id}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/d8f9f272fd8e54ad.
Report an issue: GitHub.