nautechsystems/nautilus_trader · error
Lighter spot symbol must contain one nonempty BASE/QUOTE pai
Error message
Lighter spot symbol must contain one nonempty BASE/QUOTE pair
What it means
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.
Source
Thrown at crates/adapters/lighter/src/http/parse.rs:552
.build()
.map_err(|e| anyhow::anyhow!("{e}"))?;
registry.insert(
order_book.market_id,
order_book.symbol.as_str(),
order_book.market_type,
);
Ok(InstrumentAny::CurrencyPair(instrument))
}
fn spot_symbol_currencies(symbol: &str) -> anyhow::Result<(Currency, Currency)> {
let (base, quote) = symbol
.split_once('/')
.context("Lighter spot symbol must use BASE/QUOTE format")?;
let base = base.trim();
let quote = quote.trim();
anyhow::ensure!(
!base.is_empty() && !quote.is_empty() && !quote.contains('/'),
"Lighter spot symbol must contain one nonempty BASE/QUOTE pair",
);
Ok((
Currency::get_or_create_crypto(base),
Currency::get_or_create_crypto(quote),
))
}
fn symbol_currencies(symbol: &str, default_quote: &str) -> (Currency, Currency) {
let (base, quote) = symbol.split_once('/').unwrap_or((symbol, default_quote));
(
Currency::get_or_create_crypto(base),
Currency::get_or_create_crypto(quote),
)
}
View on GitHub (pinned to 18893faf8b)
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.
Example fix
// before: assuming every market id maps to a spot pair
let (base, quote) = spot_symbol_currencies(&order_book.symbol)?;
// after: guard by market type first
if order_book.market_type == MarketType::SPOT {
let (base, quote) = spot_symbol_currencies(&order_book.symbol)?;
} Defensive patterns
Strategy: validation
Validate before calling
// Rust: pre-validate symbol format
fn is_valid_spot_symbol(s: &str) -> bool {
let parts: Vec<&str> = s.split('/').collect();
parts.len() == 2 && !parts[0].trim().is_empty() && !parts[1].trim().is_empty()
} Type guard
fn is_valid_spot_symbol(symbol: &str) -> bool {
match symbol.split_once('/') {
Some((b, q)) => !b.trim().is_empty() && !q.trim().is_empty() && !q.contains('/'),
None => false,
}
} Try / catch
let (base, quote) = match spot_symbol_currencies(&order_book.symbol) {
Ok(c) => c,
Err(e) => { warn!("skipping market {}: {e:#}", order_book.symbol); continue; }
}; Prevention
- 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).
When it happens
Trigger: 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).
Common situations: Venue API changes symbol format; market type misclassified as spot when it is actually a perp; empty base or quote fields in OrderBookDetails.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/986a208457ecea74.
Report an issue: GitHub.