nautechsystems/nautilus_trader · error

Book snapshot hash mismatch for {}: expected {expected}, com

Error message

Book snapshot hash mismatch for {}: expected {expected}, computed {computed}

What it means

Polymarket book snapshots carry a content hash computed over bids/asks with min_order_size and neg_risk adjustments. verify_book_snapshot_hash recomputes the hash; when the computed value differs from the snapshot's declared hash the snapshot cannot be trusted and processing fails loudly instead of accepting a corrupted or stale book.

Source

Thrown at crates/adapters/polymarket/src/websocket/parse.rs:82

    })?;
    Quantity::from_decimal_dp(value, precision)
}

pub(crate) fn verify_book_snapshot_hash(
    snap: &PolymarketBookSnapshot,
    min_order_size: Option<&str>,
    neg_risk: Option<bool>,
) -> anyhow::Result<bool> {
    let Some(expected) = snap.hash.as_deref() else {
        return Ok(false);
    };

    let Some(computed) = book_snapshot_hash(snap, min_order_size, neg_risk)? else {
        return Ok(false);
    };

    if computed != expected {
        anyhow::bail!(
            "Book snapshot hash mismatch for {}: expected {expected}, computed {computed}",
            snap.asset_id
        );
    }

    Ok(true)
}

fn book_snapshot_hash(
    snap: &PolymarketBookSnapshot,
    min_order_size: Option<&str>,
    neg_risk: Option<bool>,
) -> anyhow::Result<Option<String>> {
    let Some(min_order_size) = snap.min_order_size.as_deref().or(min_order_size) else {
        return Ok(None);
    };

    let Some(tick_size) = snap.tick_size.as_deref() else {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the min_order_size and neg_risk parameters passed for that asset_id match the venue's current market definition (fetch fresh market metadata)
  2. Re-fetch the snapshot over REST and reconcile before applying deltas
  3. Check for a venue-side hash algorithm change and update book_snapshot_hash accordingly
  4. Log the asset_id, expected, and computed hashes to confirm whether it is a parameter bug or transport corruption
Defensive patterns

Strategy: validation

Validate before calling

// fetch fresh market metadata before processing snapshots
let meta = fetch_market_meta(&snap.asset_id)?; // includes tick_size/min_order_size, neg_risk
verify_book_snapshot_hash(&snap, meta.min_order_size, meta.neg_risk)?;

Try / catch

match verify_book_snapshot_hash(&snap, min_order_size, neg_risk) {
    Ok(true) => apply_snapshot(snap),
    Ok(false) => log::warn!("hash not computable; requesting REST snapshot"),
    Err(e) if e.to_string().contains("hash mismatch") => resync_book_from_rest(&snap.asset_id).await?,
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: A market message of type snapshot whose asset_id's bids/asks hash to a different value than snap.hash — e.g. hash algorithm parameters (min_order_size, neg_risk) wrong for the market, or the snapshot payload was altered/truncated in transit.

Common situations: Hard-coded or stale min_order_size/neg_risk values for a market whose parameters changed; negative-risk (neg_risk) markets hashed with non-neg-risk logic; proxy/middleman corrupting frames; adapter version lagging a venue hash change.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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