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

  1. Ensure the string has the '<SYMBOL>.<VENUE>' format with non-empty symbol and venue before converting.
  2. Use `InstrumentId::from_str(...)` / `InstrumentId::checked_from_str` and handle the Err explicitly instead of the panicking `From` impl.
  3. Validate the venue against `Venue::from_str` and the symbol against `Symbol` first.
  4. 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

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/d8f9f272fd8e54ad. Report an issue: GitHub.