nautechsystems/nautilus_trader · error

provider order side {} does not match cached order side {}

Error message

provider order side {} does not match cached order side {}

What it means

validate_client_bound_order_row compares the cached OrderAny's order_side with the provider PolymarketOpenOrder's side (converted into the domain enum). A disagreement means the provider order being reconciled is not the same logical order as the cached one (or a side-mapping bug exists), so this anyhow error is raised before further checks.

Source

Thrown at crates/adapters/polymarket/src/execution/reconciliation.rs:487

    provider_order: &PolymarketOpenOrder,
    expected_quantity: Quantity,
) -> anyhow::Result<()> {
    anyhow::ensure!(
        expected_quantity.as_decimal() == provider_order.original_size,
        "provider order quantity {} does not match cached order quantity {}",
        provider_order.original_size,
        expected_quantity,
    );
    Ok(())
}

fn validate_client_bound_order_row(
    provider_order: &PolymarketOpenOrder,
    cached_order: &OrderAny,
    expected_quantity: Quantity,
    provider_expire_time: Option<UnixNanos>,
) -> anyhow::Result<()> {
    anyhow::ensure!(
        cached_order.order_side() == provider_order.side.into(),
        "provider order side {} does not match cached order side {}",
        provider_order.side,
        cached_order.order_side(),
    );
    anyhow::ensure!(
        cached_order.time_in_force() == provider_order.order_type.into(),
        "provider order time in force {} does not match cached order time in force {}",
        provider_order.order_type,
        cached_order.time_in_force(),
    );
    validate_client_bound_order_quantity(provider_order, expected_quantity)?;
    let cached_price = cached_order
        .price()
        .context("cached Limit order is missing price")?;
    anyhow::ensure!(
        cached_price.as_decimal() == provider_order.price,
        "provider order price {} does not match cached order price {cached_price}",

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the venue_order_id/provider order id mapping — the mismatch usually means the wrong provider order was matched to the cached order; correct the lookup key.
  2. Re-sync the cache by fetching the order from the provider and rebuilding the cached OrderAny with the provider's authoritative side.
  3. Check the side conversion (provider side -> OrderSide) for a mapping regression after an adapter/SDK update.
  4. Purge stale cross-strategy cache entries so ids cannot collide between strategies.

Example fix

// before
let cached = cache.get(&venue_order_id)?; // matches provider id X
// but cached.order_side() == Sell, provider side == Buy
// after
let cached = cache.get_by_both(&venue_order_id, &client_order_id)?; // disambiguate the lookup
anyhow::ensure!(cached.order_side() == provider_order.side.into(), "side mismatch");
Defensive patterns

Strategy: validation

Validate before calling

fn sides_match(provider: &PolymarketOpenOrder, cached: &OrderAny) -> bool {
    cached.order_side() == provider.side.into()
}
// match on both ids to avoid collisions:
let cached = cache.find(|o| o.venue_order_id() == provider.id && o.order_side() == provider.side.into());

Type guard

fn same_logical_order(p: &PolymarketOpenOrder, c: &OrderAny) -> bool {
    c.venue_order_id().map(|v| v.as_str() == p.id).unwrap_or(false)
        && c.order_side() == p.side.into()
}

Try / catch

match build_order_report_from_order(&provider_order, &cached) {
    Err(e) if e.to_string().contains("does not match cached order side") => {
        log::error!("wrong provider order matched to cache: {}", provider_order.id);
        // re-key the lookup, do not silently flip the side
    }
    other => other?,
}

Prevention

When it happens

Trigger: build_order_report_from_order -> validate_client_bound_order_row receives a provider order whose side (BUY/SELL) does not match cached_order.order_side() — typically because a venue_order_id collision matched two different orders, or the cached order was created with an inverted side.

Common situations: Venue order id reuse or off-by-one when matching provider order ids to cached orders; cache restored from a different account/strategy; side conversion bug after a provider API enum change (e.g. long vs buy); orders duplicated across strategies sharing one order id namespace.

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/5efa6b4064d47102. Report an issue: GitHub.