nautechsystems/nautilus_trader · error

RPC tick {tick_value} does not match positions: derived gros

Error message

RPC tick {tick_value} does not match positions: derived gross={liquidity_gross} net={liquidity_net}, received gross={} net={}

What it means

This error is raised by validate_rpc_snapshot_topology when a tick fetched from the RPC node does not match the liquidity gross/net values that were derived from the position set. It indicates the RPC snapshot and locally derived liquidity state are inconsistent, which would corrupt liquidity-math derived from positions. The library fails closed rather than building a snapshot from contradictory data.

Source

Thrown at crates/adapters/blockchain/src/data/core.rs:2242

        }

        if derived_ticks.len() != actual_ticks.len() {
            anyhow::bail!(
                "RPC tick topology does not match positions: derived {} ticks, received {}",
                derived_ticks.len(),
                actual_ticks.len()
            );
        }

        for (tick_value, (liquidity_gross, liquidity_net)) in derived_ticks {
            let actual_tick = actual_ticks
                .get(&tick_value)
                .with_context(|| format!("RPC snapshot omitted position boundary {tick_value}"))?;

            if actual_tick.liquidity_gross != liquidity_gross
                || actual_tick.liquidity_net != liquidity_net
            {
                anyhow::bail!(
                    "RPC tick {tick_value} does not match positions: derived gross={liquidity_gross} net={liquidity_net}, received gross={} net={}",
                    actual_tick.liquidity_gross,
                    actual_tick.liquidity_net
                );
            }
        }

        Ok(())
    }

    fn timestamp_for_on_chain_snapshot(
        profiler: &PoolProfiler,
        cached_timestamp: Option<UnixNanos>,
    ) -> anyhow::Result<UnixNanos> {
        cached_timestamp
            .or(profiler.last_processed_ts)
            .context("missing block timestamp for on-chain snapshot")
    }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Re-fetch the RPC snapshot at the exact block hash/number used for position derivation and retry
  2. Verify the cache's pool events (mint/burn) are fully synced up to the snapshot block before deriving tick liquidity
  3. Check for chain reorgs and rebuild the local cache from a canonical block
  4. Pin a consistent archive RPC endpoint (same node, pinned block) instead of a load-balanced set
  5. Log both derived and received tick values and compare per-tick to identify which ticks diverge

Example fix

// before: deriving positions and fetching snapshot at different points in time
let snapshot = client.fetch_tick_snapshot(pool, latest_block()).await?;
validate_rpc_snapshot_topology(&cache, &snapshot)?;
// after: pin both to the same block
let block = latest_block();
let snapshot = client.fetch_tick_snapshot_at(pool, block).await?;
validate_rpc_snapshot_topology(&cache_at_block(&cache, block), &snapshot)?;
Defensive patterns

Strategy: validation

Validate before calling

let actual = rpc_snapshot.ticks.get(&tick_value).ok_or_else(|| anyhow!("tick {tick_value} missing"))?;
if actual.liquidity_gross != derived_gross || actual.liquidity_net != derived_net {
    return Err(anyhow!("tick {} mismatch at block {}", tick_value, snapshot_block));
}

Type guard

fn ticks_consistent(t: &RpcTick, gross: u128, net: i128) -> bool {
    t.liquidity_gross == gross && t.liquidity_net == net
}

Prevention

When it happens

Trigger: Calling snapshot/position topology validation (validate_rpc_snapshot_topology) when the tick at tick_value exists in the RPC response but its liquidity_gross or liquidity_net differs from the values computed from cached positions — e.g. the RPC node served a state at a different block, the position derivation used stale events, or the node is behind/reorged.

Common situations: RPC endpoint lagging behind the block used for position derivation; a reorg between the event sync and the snapshot fetch; an archive node with truncated or pruned tick state; concurrent updates to the cache during snapshot validation; misconfigured RPC provider with inconsistent replicas behind a load balancer.

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