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
- 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.
- Re-sync the cache by fetching the order from the provider and rebuilding the cached OrderAny with the provider's authoritative side.
- Check the side conversion (provider side -> OrderSide) for a mapping regression after an adapter/SDK update.
- 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
- Look up cached orders by full (client_order_id, venue_order_id) keys, never side-agnostic id only
- Separate order id namespaces per strategy/account to avoid cache collisions
- Add a unit test for the provider side -> OrderSide conversion when upgrading the adapter SDK
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
- provider maker order {} side {provider_side} contradicts der
- provider order quantity {} does not match cached order quant
- provider order time in force {} does not match cached order
- unmapped_in_scope_message("open order", instrument_id, Some(
- unmapped_in_scope_message("position", instrument_id, None, c
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/5efa6b4064d47102.
Report an issue: GitHub.