nautechsystems/nautilus_trader · error

symbol checked for suffix during construction

Error message

symbol checked for suffix during construction

What it means

BybitSymbol::product_type derives the product type from the symbol suffix (-SPOT, -LINEAR, -INVERSE, -OPTION). BybitSymbol::new validates the suffix at construction, so from_suffix is guaranteed to succeed here; the expect encodes that guarantee. A panic means a BybitSymbol was built without going through new (e.g. via unsafe/struct literal or a constructor change).

Source

Thrown at crates/adapters/bybit/src/common/symbol.rs:77

    }

    /// Returns the underlying symbol without the Bybit suffix.
    #[must_use]
    pub fn raw_symbol(&self) -> &str {
        self.value
            .rsplit_once('-')
            .map_or(self.value.as_str(), |(prefix, _)| prefix)
    }

    /// Returns the product type identified by the suffix.
    ///
    /// # Panics
    ///
    /// Panics if the symbol has no valid suffix (unreachable after construction).
    #[must_use]
    pub fn product_type(&self) -> BybitProductType {
        BybitProductType::from_suffix(self.value.as_str())
            .expect("symbol checked for suffix during construction")
    }

    /// Returns the instrument identifier corresponding to this symbol.
    #[must_use]
    pub fn to_instrument_id(&self) -> InstrumentId {
        InstrumentId::new(Symbol::from_ustr_unchecked(self.value), *BYBIT_VENUE)
    }

    /// Returns the symbol value as `Ustr`.
    #[must_use]
    pub fn as_ustr(&self) -> Ustr {
        self.value
    }
}

impl Display for BybitSymbol {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(self.value.as_str())

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Always construct BybitSymbol via BybitSymbol::new (or serde using new)
  2. Keep VALID_SUFFIXES and BybitProductType::from_suffix in sync when adding product types
  3. Add a unit test asserting product_type() for every valid suffix
  4. Validate user symbols with BybitSymbol::new and surface the error instead of bypassing it

Example fix

// before
let sym = BybitSymbol { value: Ustr::from("BTCUSDT") }; // bypasses validation
let pt = sym.product_type(); // panics
// after
let sym = BybitSymbol::new("BTCUSDT-SPOT")?;
let pt = sym.product_type(); // BybitProductType::Spot
Defensive patterns

Strategy: validation

Validate before calling

let sym = BybitSymbol::new(user_input)?; // validates suffix up front
let product_type = sym.product_type(); // safe now

Type guard

fn has_valid_bybit_suffix(value: &str) -> bool {
    ["-SPOT", "-LINEAR", "-INVERSE", "-OPTION"]
        .iter()
        .any(|s| value.to_uppercase().contains(s))
}

Try / catch

match BybitSymbol::new(raw) {
    Ok(sym) => use(sym),
    Err(e) => eprintln!("invalid Bybit symbol: {e}"),
}

Prevention

When it happens

Trigger: Calling product_type() on a BybitSymbol constructed by bypassing BybitSymbol::new, or after changing VALID_SUFFIXES so it disagrees with BybitProductType::from_suffix's accepted suffixes.

Common situations: Maintainer adds a new suffix to one list but not the other; test code building BybitSymbol directly; deserialization path constructing the struct without validation.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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