nautechsystems/nautilus_trader · error

SPOT instrument should have base currency

Error message

SPOT instrument should have base currency

What it means

When parsing a SPOT wallet/balance response, the code looks up the instrument in `instruments_cache` and calls `.base_currency().expect(...)` because SPOT instruments are required to carry a base currency. Panicking means the cached entry is not a SPOT instrument with a base currency — e.g. the cache holds a wrong/missing instrument for the symbol, or the symbol being iterated is not actually SPOT.

Source

Thrown at crates/adapters/bybit/src/http/client.rs:2452

        for wallet in &response.result.list {
            for coin_balance in &wallet.coin {
                let balance = coin_balance.wallet_balance - coin_balance.spot_borrow;
                *wallet_by_coin
                    .entry(coin_balance.coin)
                    .or_insert(Decimal::ZERO) += balance;
            }
        }

        let mut reports = Vec::new();

        if let Some(instrument) = self
            .instruments_cache
            .get_cloned(&instrument_id.symbol.inner())
        {
            let base_currency = instrument
                .base_currency()
                .expect("SPOT instrument should have base currency");
            let coin = base_currency.code;
            let wallet_balance = wallet_by_coin.get(&coin).copied().unwrap_or(Decimal::ZERO);

            let side = if wallet_balance > Decimal::ZERO {
                PositionSide::Long
            } else if wallet_balance < Decimal::ZERO {
                PositionSide::Short
            } else {
                PositionSide::Flat
            };

            let abs_balance = wallet_balance.abs();
            let quantity = Quantity::from_decimal_dp(abs_balance, instrument.size_precision())?;

            let report = PositionStatusReport::new(
                account_id,
                instrument_id,
                side,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the instruments cache is fully loaded (exchange-info fetch) before requesting SPOT wallet data
  2. Verify the symbol in the cache corresponds to a live SPOT instrument and refresh the cache if stale
  3. Skip or log symbols missing from the cache instead of expecting
  4. Report a bug if a valid SPOT balance request triggers the panic

Example fix

// before
let base_currency = instrument.base_currency().expect("SPOT instrument should have base currency");
// after
let Some(base_currency) = instrument.base_currency() else {
    log::warn!("SPOT instrument {instrument_id} missing base currency; skipping");
    continue;
};
Defensive patterns

Strategy: validation

Validate before calling

// Before requesting SPOT balances, ensure the instruments cache covers the symbol
assert!(client.has_instrument(&instrument_id.symbol.inner()), "instrument not cached; load instruments first");

Type guard

fn is_spot_with_base(i: &dyn AnyInstrument) -> bool {
    i.instrument_class() == InstrumentClass::Spot && i.base_currency().is_some()
}

Try / catch

// If extending the client, replace expect with graceful skip:
let Some(base) = instrument.base_currency() else { log::warn!("missing base for {sym}"); return Ok(()); };

Prevention

When it happens

Trigger: Requesting SPOT account balances when the instruments cache lacks the symbol or contains a non-SPOT/stale instrument entry for it, so `base_currency()` returns `None`.

Common situations: Instruments cache not loaded/refreshed before balance parsing; a delisted or renamed symbol still present in wallet data; a bug where non-SPOT symbols leak into the SPOT wallet parsing path.

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/565559405acbc41d. Report an issue: GitHub.