{"record":{"id":"52c31afefc336a71","repo":"nautechsystems/nautilus_trader","slug":"symbol-checked-for-suffix-during-construction","errorCode":null,"errorMessage":"symbol checked for suffix during construction","messagePattern":"symbol checked for suffix during construction","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/bybit/src/common/symbol.rs","lineNumber":77,"sourceCode":"    }\n\n    /// Returns the underlying symbol without the Bybit suffix.\n    #[must_use]\n    pub fn raw_symbol(&self) -> &str {\n        self.value\n            .rsplit_once('-')\n            .map_or(self.value.as_str(), |(prefix, _)| prefix)\n    }\n\n    /// Returns the product type identified by the suffix.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the symbol has no valid suffix (unreachable after construction).\n    #[must_use]\n    pub fn product_type(&self) -> BybitProductType {\n        BybitProductType::from_suffix(self.value.as_str())\n            .expect(\"symbol checked for suffix during construction\")\n    }\n\n    /// Returns the instrument identifier corresponding to this symbol.\n    #[must_use]\n    pub fn to_instrument_id(&self) -> InstrumentId {\n        InstrumentId::new(Symbol::from_ustr_unchecked(self.value), *BYBIT_VENUE)\n    }\n\n    /// Returns the symbol value as `Ustr`.\n    #[must_use]\n    pub fn as_ustr(&self) -> Ustr {\n        self.value\n    }\n}\n\nimpl Display for BybitSymbol {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(self.value.as_str())","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/bybit/src/common/symbol.rs#L59-L95","documentation":"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).","triggerScenarios":"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.","commonSituations":"Maintainer adds a new suffix to one list but not the other; test code building BybitSymbol directly; deserialization path constructing the struct without validation.","solutions":["Always construct BybitSymbol via BybitSymbol::new (or serde using new)","Keep VALID_SUFFIXES and BybitProductType::from_suffix in sync when adding product types","Add a unit test asserting product_type() for every valid suffix","Validate user symbols with BybitSymbol::new and surface the error instead of bypassing it"],"exampleFix":"// before\nlet sym = BybitSymbol { value: Ustr::from(\"BTCUSDT\") }; // bypasses validation\nlet pt = sym.product_type(); // panics\n// after\nlet sym = BybitSymbol::new(\"BTCUSDT-SPOT\")?;\nlet pt = sym.product_type(); // BybitProductType::Spot","handlingStrategy":"validation","validationCode":"let sym = BybitSymbol::new(user_input)?; // validates suffix up front\nlet product_type = sym.product_type(); // safe now","typeGuard":"fn has_valid_bybit_suffix(value: &str) -> bool {\n    [\"-SPOT\", \"-LINEAR\", \"-INVERSE\", \"-OPTION\"]\n        .iter()\n        .any(|s| value.to_uppercase().contains(s))\n}","tryCatchPattern":"match BybitSymbol::new(raw) {\n    Ok(sym) => use(sym),\n    Err(e) => eprintln!(\"invalid Bybit symbol: {e}\"),\n}","preventionTips":["Always construct BybitSymbol via new, never by struct literal","Keep suffix lists and BybitProductType::from_suffix in sync","Normalize user symbols to uppercase before validation"],"tags":["panic","invariant","symbol","bybit"],"backgroundTag":"internal-invariant-violation","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"}