{"record":{"id":"986a208457ecea74","repo":"nautechsystems/nautilus_trader","slug":"lighter-spot-symbol-must-contain-one-nonempty-base","errorCode":null,"errorMessage":"Lighter spot symbol must contain one nonempty BASE/QUOTE pair","messagePattern":"Lighter spot symbol must contain one nonempty BASE/QUOTE pair","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/lighter/src/http/parse.rs","lineNumber":552,"sourceCode":"        .build()\n        .map_err(|e| anyhow::anyhow!(\"{e}\"))?;\n\n    registry.insert(\n        order_book.market_id,\n        order_book.symbol.as_str(),\n        order_book.market_type,\n    );\n\n    Ok(InstrumentAny::CurrencyPair(instrument))\n}\n\nfn spot_symbol_currencies(symbol: &str) -> anyhow::Result<(Currency, Currency)> {\n    let (base, quote) = symbol\n        .split_once('/')\n        .context(\"Lighter spot symbol must use BASE/QUOTE format\")?;\n    let base = base.trim();\n    let quote = quote.trim();\n    anyhow::ensure!(\n        !base.is_empty() && !quote.is_empty() && !quote.contains('/'),\n        \"Lighter spot symbol must contain one nonempty BASE/QUOTE pair\",\n    );\n\n    Ok((\n        Currency::get_or_create_crypto(base),\n        Currency::get_or_create_crypto(quote),\n    ))\n}\n\nfn symbol_currencies(symbol: &str, default_quote: &str) -> (Currency, Currency) {\n    let (base, quote) = symbol.split_once('/').unwrap_or((symbol, default_quote));\n    (\n        Currency::get_or_create_crypto(base),\n        Currency::get_or_create_crypto(quote),\n    )\n}\n","sourceCodeStart":534,"sourceCodeEnd":570,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/lighter/src/http/parse.rs#L534-L570","documentation":"Lighter spot symbols must be a single BASE/QUOTE pair, both sides nonempty, with no further '/'. spot_symbol_currencies validates this before creating currencies; any violation raises this ensure! error so a malformed symbol cannot silently create wrong currency pairs.","triggerScenarios":"parse_spot_instrument receives an OrderBookDetails entry whose base_currency/quote_currency-derived symbol is like 'ETH/', '/USDC', 'ETH/USDC/extra', or has no '/' at all (that case surfaces as the sibling 'must use BASE/QUOTE format' context).","commonSituations":"Venue API changes symbol format; market type misclassified as spot when it is actually a perp; empty base or quote fields in OrderBookDetails.","solutions":["Verify the market is actually SPOT type before routing to parse_spot_instrument.","Check the Lighter API response's base/quote fields are populated and joined as 'BASE/QUOTE'.","Add/refresh the adapter mapping if the venue changed its symbol convention.","Skip the malformed market and log it for investigation."],"exampleFix":"// before: assuming every market id maps to a spot pair\nlet (base, quote) = spot_symbol_currencies(&order_book.symbol)?;\n// after: guard by market type first\nif order_book.market_type == MarketType::SPOT {\n    let (base, quote) = spot_symbol_currencies(&order_book.symbol)?;\n}","handlingStrategy":"validation","validationCode":"// Rust: pre-validate symbol format\nfn is_valid_spot_symbol(s: &str) -> bool {\n    let parts: Vec<&str> = s.split('/').collect();\n    parts.len() == 2 && !parts[0].trim().is_empty() && !parts[1].trim().is_empty()\n}","typeGuard":"fn is_valid_spot_symbol(symbol: &str) -> bool {\n    match symbol.split_once('/') {\n        Some((b, q)) => !b.trim().is_empty() && !q.trim().is_empty() && !q.contains('/'),\n        None => false,\n    }\n}","tryCatchPattern":"let (base, quote) = match spot_symbol_currencies(&order_book.symbol) {\n    Ok(c) => c,\n    Err(e) => { warn!(\"skipping market {}: {e:#}\", order_book.symbol); continue; }\n};","preventionTips":["Check market_type is SPOT before spot parsing.","Sanitize/validate symbols received from the venue API.","Add fixture tests for malformed symbols (empty sides, extra slashes)."],"tags":["rust","symbol","validation","spot"],"backgroundTag":"invalid-argument-format","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"}