nautechsystems/nautilus_trader · error

Cannot process partial quote for {instrument_id}: missing bi

Error message

Cannot process partial quote for {instrument_id}: missing bid_size and no cached value

What it means

Partial-quote merge guard in the quote cache's process: a new BBO update omitted bid_size and no previously cached quote exists for the instrument, so a complete QuoteTick cannot be assembled and the update is rejected.

Source

Thrown at crates/common/src/cache/quote.rs:140

        ts_init: UnixNanos,
    ) -> anyhow::Result<QuoteTick> {
        let cached = self.quotes.get(&instrument_id);

        // Resolve each field: use provided value or fall back to cache
        let Some(bid_price) = bid_price.or_else(|| cached.map(|quote| quote.bid_price)) else {
            anyhow::bail!(
                "Cannot process partial quote for {instrument_id}: missing bid_price and no cached value"
            );
        };

        let Some(ask_price) = ask_price.or_else(|| cached.map(|quote| quote.ask_price)) else {
            anyhow::bail!(
                "Cannot process partial quote for {instrument_id}: missing ask_price and no cached value"
            );
        };

        let Some(bid_size) = bid_size.or_else(|| cached.map(|quote| quote.bid_size)) else {
            anyhow::bail!(
                "Cannot process partial quote for {instrument_id}: missing bid_size and no cached value"
            );
        };

        let Some(ask_size) = ask_size.or_else(|| cached.map(|quote| quote.ask_size)) else {
            anyhow::bail!(
                "Cannot process partial quote for {instrument_id}: missing ask_size and no cached value"
            );
        };

        let quote = QuoteTick::new(
            instrument_id,
            bid_price,
            ask_price,
            bid_size,
            ask_size,
            ts_event,
            ts_init,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Send a full quote (including both sizes) as the first update per instrument
  2. Skip or buffer partial updates until the cache holds a quote for that instrument
  3. Supply a default/explicit bid_size when no cached value exists

Example fix

// before
quote_cache.process(instrument_id, bid, ask, None, ask_size, ts_event, ts_init)?;
// after
let bid_size = bid_size.or_else(|| last_known_sizes.get(&instrument_id).copied());
quote_cache.process(instrument_id, bid, ask, bid_size, ask_size, ts_event, ts_init)?;
Defensive patterns

Strategy: try-catch

Validate before calling

fn can_process(cache: &QuoteCache, id: &InstrumentId, bid_size: Option<Quantity>) -> bool {
    bid_size.is_some() || cache.contains(id)
}

Try / catch

match quote_cache.process(instrument_id, bid_price, ask_price, bid_size, ask_size, ts_event, ts_init) {
    Ok(quote) => /* forward quote */,
    Err(e) if e.to_string().contains("missing bid_size") => { /* skip partial update */ }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: First call to process for an instrument (or the first call after clear()) with bid_size=None while other fields are provided.

Common situations: Size-only-delta feeds that never send sizes on the first tick; restarted processes with an empty cache; feeds where size fields are optional/absent at session start.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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