nautechsystems/nautilus_trader · error

provider maker order {} side {provider_side} contradicts der

Error message

provider maker order {} side {provider_side} contradicts derived side {derived_side}

What it means

When deriving an order's side from a fill/trade, the code validates the side reported by the provider REST endpoint for the maker order. This ensure! fires when OrderSide::from(provider REST side) differs from the side derived from the trade data, so the two sources of truth disagree.

Source

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

        trade.trader_side,
    );
    Ok(true)
}

fn validate_maker_order_side(
    trade: &PolymarketTradeReport,
    maker_order: &PolymarketMakerOrder,
) -> anyhow::Result<OrderSide> {
    let derived_side = determine_order_side(
        trade.trader_side,
        trade.side,
        trade.asset_id.as_str(),
        maker_order.asset_id.as_str(),
    );
    let provider_side = maker_order
        .side
        .with_context(|| format!("REST maker order {} is missing side", maker_order.order_id))?;
    anyhow::ensure!(
        OrderSide::from(provider_side) == derived_side,
        "provider maker order {} side {provider_side} contradicts derived side {derived_side}",
        maker_order.order_id,
    );
    Ok(derived_side)
}

fn checked_venue_order_id(value: &str, evidence: &str) -> anyhow::Result<VenueOrderId> {
    VenueOrderId::new_checked(value)
        .with_context(|| format!("{evidence} has invalid venue order ID {value:?}"))
}

fn checked_trade_id(value: &str, evidence: &str) -> anyhow::Result<TradeId> {
    TradeId::new_checked(value)
        .with_context(|| format!("{evidence} has invalid trade ID {value:?}"))
}

#[derive(Clone, Copy)]

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Print both values and check how OrderSide::from maps the provider side string; verify the mapping matches Polymarket's BUY/SELL semantics.
  2. Re-fetch the maker order from REST to rule out staleness.
  3. Check whether the derived side logic uses the correct perspective (own order vs counterparty) in the trade payload.
  4. Update the side mapping or reconciliation logic after confirming which source is authoritative.

Example fix

// before
let derived = derive_side_from_trade(trade);
validate_maker_order_side(maker_order, derived)?;
// after: normalize provider side explicitly before comparing
let normalized = match maker_order.side.as_str() {
    "BUY" => OrderSide::Buy,
    "SELL" => OrderSide::Sell,
    other => anyhow::bail!("unknown provider side {other}"),
};
anyhow::ensure!(normalized == derived, ...);
Defensive patterns

Strategy: validation

Validate before calling

fn sides_agree(provider_side: &str, derived: OrderSide) -> bool {
    OrderSide::from(provider_side) == derived
}

Type guard

fn normalize_provider_side(s: &str) -> Option<OrderSide> {
    match s.to_ascii_uppercase().as_str() {
        "BUY" => Some(OrderSide::Buy),
        "SELL" => Some(OrderSide::Sell),
        _ => None,
    }
}

Try / catch

let derived_side = match validate_maker_order_side(&maker_order, &trade) {
    Ok(side) => side,
    Err(e) => { warn!("side mismatch for order {}: {e}", maker_order.order_id); refresh_order_and_retry()? }
};

Prevention

When it happens

Trigger: classify_target_trade or build_fill_reports_from_trades -> validate_maker_order_side fetches the maker order via REST; its side field (e.g. BUY/SELL) maps to a different OrderSide than the one derived from the trade's asset/role data.

Common situations: Polymarket REST and data-api endpoints disagreeing (one reporting the order side, the other the counter-side); parsing BUY/SELL enums case- or semantically-differently; stale cached REST order vs fresh trade.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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