nautechsystems/nautilus_trader · error

Cannot subscribe for synthetic instrument `OrderBookDelta` d

Error message

Cannot subscribe for synthetic instrument `OrderBookDelta` data

What it means

subscribe_book_deltas rejects OrderBookDelta subscriptions for synthetic instrument IDs. Order book deltas come from a venue data client; synthetic instruments are locally computed and have no order book stream, so the subscription is invalid.

Source

Thrown at crates/data/src/engine/mod.rs:3115

            .iter()
            .filter(|(_, sid)| **sid == series_id)
            .map(|(id, _)| *id)
            .collect();

        for id in &instrument_ids {
            self.option_chain_instrument_index.remove(id);
            manager_rc.borrow_mut().handle_instrument_expired(id);
        }

        manager_rc.borrow_mut().teardown(&self.clock);
        self.option_chain_managers.remove(&series_id);

        log::info!("Proactively torn down expired option chain {series_id}");
    }

    fn subscribe_book_deltas(&mut self, cmd: &SubscribeBookDeltas) -> anyhow::Result<bool> {
        if cmd.instrument_id.is_synthetic() {
            anyhow::bail!("Cannot subscribe for synthetic instrument `OrderBookDelta` data");
        }

        // Validate parent shape BEFORE mutating subscription state so a parse
        // failure leaves the engine bookkeeping unchanged.
        let parent = resolve_parent_components(&cmd.instrument_id, cmd.params.as_ref())?;

        let had_deltas =
            self.has_book_delta_subscription_key(cmd.instrument_id, cmd.client_id, cmd.venue);

        if cmd.managed {
            self.setup_book_updater(&cmd.instrument_id, cmd.book_type, true, parent)?;
        }

        self.increment_book_delta_subscription(cmd.instrument_id, cmd.client_id, cmd.venue);

        Ok(!had_deltas)
    }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Subscribe to book deltas only for real venue-listed instrument IDs
  2. Check instrument_id.is_synthetic() before issuing the book-delta subscription
  3. If synthetic price streams are needed, use the SyntheticInstrument API / synthetic quote data instead of an order book subscription
  4. Fix config where a synthetic ID string (containing '=' synthetic notation) was pasted as the instrument ID

Example fix

// before
engine.subscribe_book_deltas(SubscribeBookDeltas::new(synthetic_id, client_id, venue));
// after
anyhow::ensure!(!synthetic_id.is_synthetic(), "book deltas require a venue instrument");
engine.subscribe_book_deltas(SubscribeBookDeltas::new(synthetic_id, client_id, venue));
Defensive patterns

Strategy: validation

Validate before calling

if instrument_id.is_synthetic() {
    // no order book stream exists for synthetic instruments
    return Ok(());
}

Type guard

fn supports_book_data(id: &InstrumentId) -> bool {
    !id.is_synthetic()
}

Try / catch

match engine.subscribe_book_deltas(&cmd) {
    Err(e) if e.to_string().contains("synthetic") => {
        log::error!("synthetic ID used for book deltas: {e}");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling DataEngine.subscribe_book_deltas (or subscribe_order_book_deltas) with a cmd.instrument_id where is_synthetic() is true — e.g. an ID from SyntheticInstrument.

Common situations: Pointing a book-delta subscription config at a synthetic instrument by mistake; programmatically generating instrument IDs and losing track of which are synthetic; copying subscription setup from a real instrument to a synthetic one.

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/9fb43b0d084fa7c4. Report an issue: GitHub.