nautechsystems/nautilus_trader · error · anyhow::Error

Binance Spot supports L1_MBP and L2_MBP order book…

Error message

Binance Spot supports L1_MBP and L2_MBP order book subscriptions

What it means

`subscribe_book_deltas` for Binance Spot only supports depth-1 (L1_MBP, top-of-book) and full L2 depth (L2_MBP) order book subscriptions. Any other book type (e.g. L3_MBP or depth variants Spot does not offer) is rejected with this error before any subscription is made.

Solutions

  1. Set the subscription book_type to `BookType::L2_MBP` (or L1_MBP) for Binance Spot instruments
  2. Remove any existing L1_MBP subscription for the instrument before requesting L2_MBP, as they are mutually exclusive
  3. Run the strategy against Binance USDT-M Futures instead if a deeper/other book type is required
  4. Add an instrument/exchange guard in strategy config so non-Spot book types are only requested on supported venues

Example fix

// before
subscribe_book_deltas(instrument_id, BookType::L3_MBP);
// after
subscribe_book_deltas(instrument_id, BookType::L2_MBP); // Spot supports L1_MBP/L2_MBP only
Defensive patterns

Strategy: validation

Validate before calling

fn validate_spot_book_sub(book_type: BookType, instrument_id: InstrumentId,
                           l1_subs: &HashSet<InstrumentId>) -> Result<(), String> {
    match book_type {
        BookType::L1_MBP | BookType::L2_MBP => {},
        other => return Err(format!("{other:?} not supported on Binance Spot")),
    }
    if book_type == BookType::L2_MBP && l1_subs.contains(&instrument_id) {
        return Err(format!("L1 already subscribed for {instrument_id}; unsubscribe first"));
    }
    Ok(())
}

Type guard

fn is_spot_supported_book_type(bt: BookType) -> bool {
    matches!(bt, BookType::L1_MBP | BookType::L2_MBP)
}

Try / catch

match data_engine.subscribe_book_deltas(cmd).await {
    Err(e) if e.to_string().contains("L1_MBP and L2_MBP") => {
        log::warn!("falling back to L2_MBP for {instrument_id}");
        data_engine.subscribe_book_deltas(cmd.with_book_type(BookType::L2_MBP)).await?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling `subscribe_book_deltas` with a subscription command whose `book_type` is neither `BookType::L1_MBP` nor `BookType::L2_MBP`; also rejected if an L1 subscription already exists for the instrument and an L2 subscribe is requested (mutually exclusive per instrument).

Common situations: Porting a strategy configured for futures (which supports other depth modes) to Spot; a generic data-engine config requesting L3/diff depth on a Spot instrument; mixing L1 and L2 subscriptions for the same instrument in one strategy.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at crates/adapters/binance/src/spot/data.rs:1848

    fn subscribe_book_deltas(&mut self, cmd: SubscribeBookDeltas) -> anyhow::Result<()> {
        if cmd.book_type == BookType::L1_MBP {
            anyhow::ensure!(
                cmd.depth.is_none_or(|depth| depth.get() == 1),
                "Binance Spot L1_MBP supports depth 1 only"
            );
            anyhow::ensure!(
                !self.book_subscriptions.contains_key(&cmd.instrument_id),
                "cannot subscribe L1_MBP and L2_MBP for the same Binance Spot instrument"
            );
            self.l1_book_subscriptions.rcu(|subscriptions| {
                *subscriptions.entry(cmd.instrument_id).or_insert(0) += 1;
            });
            self.subscribe_top_of_book(cmd.instrument_id);
            return Ok(());
        }

        if cmd.book_type != BookType::L2_MBP {
            anyhow::bail!("Binance Spot supports L1_MBP and L2_MBP order book subscriptions");
        }
        anyhow::ensure!(
            !self.l1_book_subscriptions.contains_key(&cmd.instrument_id),
            "cannot subscribe L1_MBP and L2_MBP for the same Binance Spot instrument"
        );

        let instrument_id = cmd.instrument_id;
        let ws = self.ws_client.clone();
        let symbol_lower = instrument_id.symbol.as_str().to_lowercase();

        if self.spot_market_data_mode == BinanceSpotMarketDataMode::Json && cmd.depth.is_some() {
            // Explicit depth requests use partial-book streams. Full-depth JSON
            // subscriptions fall through to the REST snapshot + @depth diff path.
            let depth_level = match cmd.depth.map(|d| d.get()) {
                Some(1..=5) => 5,
                Some(6..=10) => 10,
                _ => 20,
            };

View on GitHub (pinned to 18893faf8b)