nautechsystems/nautilus_trader · error

provider trade {} repeats with contradictory evidence

Error message

provider trade {} repeats with contradictory evidence

What it means

During trade reconciliation, `admit_selected_trade` deduplicates provider trade reports by trade id. If a trade id is seen a second time, the report must be byte-for-byte identical to the first; any differing field means the provider returned contradictory evidence for the same trade, so reconciliation aborts with `anyhow::ensure!` instead of silently picking one version.

Source

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

    /// Whether valid unsettled evidence for the requested venue order was found.
    pub has_pending_target: bool,
    /// Fill entries dropped because their instrument is not loaded.
    pub unmapped_instruments: usize,
    /// In-scope historical fills dropped because their instrument is not loaded.
    pub in_scope_historical: usize,
    /// Confirmed maker trades dropped because no maker order in the match is
    /// owned by the account.
    pub unowned_maker_trades: usize,
    /// Confirmed trades dropped from a bounded report because their event time is invalid.
    pub untimestamped_trades: usize,
}

fn admit_selected_trade<'a>(
    selected_trades: &mut AHashMap<&'a str, &'a PolymarketTradeReport>,
    trade: &'a PolymarketTradeReport,
) -> anyhow::Result<bool> {
    if let Some(previous) = selected_trades.get(trade.id.as_str()) {
        anyhow::ensure!(
            *previous == trade,
            "provider trade {} repeats with contradictory evidence",
            trade.id,
        );
        return Ok(false);
    }

    selected_trades.insert(trade.id.as_str(), trade);
    Ok(true)
}

/// Converts trade reports into fill reports: single implementation of maker/taker
/// parsing used by both `generate_fill_reports()` and `generate_mass_status()`.
pub(crate) fn build_fill_reports_from_trades(
    trades: &[PolymarketTradeReport],
    ctx: &FillContext<'_>,
    instruments: &AtomicMap<Ustr, InstrumentAny>,
    scope: FillReportScope,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Re-fetch the trade reports in a single pass and confirm the provider data is stable; if the venue genuinely amended the trade, rebuild/refresh the local execution state before reconciling.
  2. Check for stale caching or overlapping pagination in the code that collects PolymarketTradeReport items; deduplicate only identical payloads before calling reconciliation.
  3. Compare the two conflicting payloads (log the diff of the PolymarketTradeReport fields) to identify which field disagrees, then verify against the Polymarket Data API which version is authoritative.
  4. If the adapter version predates a venue data-amendment behavior, upgrade the nautilus_polymarket adapter to a version that reconciles amended trades instead of failing.

Example fix

// before: reconciliation hard-fails on an amended provider trade
anyhow::ensure!(
    *previous == trade,
    "provider trade {} repeats with contradictory evidence",
    trade.id,
);
// after: detect and refresh stale snapshots upstream so duplicates are identical
let mut deduped: AHashMap<&str, &PolymarketTradeReport> = AHashMap::new();
for trade in fetched_trades {
    deduped.entry(trade.id.as_str())
        .and_modify(|prev| assert_eq!(*prev, trade, "re-fetch trade {} for stable snapshot", trade.id))
        .or_insert(trade);
}
Defensive patterns

Strategy: validation

Validate before calling

let mut seen: AHashMap<&str, &PolymarketTradeReport> = AHashMap::new();
for trade in trades {
    if let Some(prev) = seen.get(trade.id.as_str()) {
        if *prev != trade {
            return Err(anyhow::anyhow!(
                "trade {} payload changed between fetches; refresh snapshot before reconciling",
                trade.id,
            ));
        }
    } else {
        seen.insert(trade.id.as_str(), trade);
    }
}

Type guard

fn is_stable_repeat<'a>(prev: &&'a PolymarketTradeReport, next: &'a PolymarketTradeReport) -> bool {
    *prev == next
}

Try / catch

match reconciliation_result {
    Err(e) if e.to_string().contains("repeats with contradictory evidence") => {
        log::warn!("provider data changed mid-reconciliation; re-fetching trades");
        retry_with_fresh_snapshot();
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling Polymarket fill/trade reconciliation (e.g. generate_fill_reports / generate_order_status_reports flow that admits selected trades) when the provider returns two report payloads with the same trade id but differing fields (different price, size, side, timestamp, or status), typically across multiple pages/queries whose data was updated in between fetches.

Common situations: Provider API pagination overlapping while the venue retroactively amended a trade; a partially settled trade whose fields changed between two reconciliation fetches; caching stale report snapshots and re-admitting them after new data arrived; replaying the same trade with corrected data after a venue settlement adjustment.

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