{"record":{"id":"46fc177adc30fd41","repo":"nautechsystems/nautilus_trader","slug":"rpc-tick-tick-value-does-not-match-positions-de","errorCode":null,"errorMessage":"RPC tick {tick_value} does not match positions: derived gross={liquidity_gross} net={liquidity_net}, received gross={} net={}","messagePattern":"RPC tick (.+?) does not match positions: derived gross=(.+?) net=(.+?), received gross=(.+?) net=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/data/core.rs","lineNumber":2242,"sourceCode":"        }\n\n        if derived_ticks.len() != actual_ticks.len() {\n            anyhow::bail!(\n                \"RPC tick topology does not match positions: derived {} ticks, received {}\",\n                derived_ticks.len(),\n                actual_ticks.len()\n            );\n        }\n\n        for (tick_value, (liquidity_gross, liquidity_net)) in derived_ticks {\n            let actual_tick = actual_ticks\n                .get(&tick_value)\n                .with_context(|| format!(\"RPC snapshot omitted position boundary {tick_value}\"))?;\n\n            if actual_tick.liquidity_gross != liquidity_gross\n                || actual_tick.liquidity_net != liquidity_net\n            {\n                anyhow::bail!(\n                    \"RPC tick {tick_value} does not match positions: derived gross={liquidity_gross} net={liquidity_net}, received gross={} net={}\",\n                    actual_tick.liquidity_gross,\n                    actual_tick.liquidity_net\n                );\n            }\n        }\n\n        Ok(())\n    }\n\n    fn timestamp_for_on_chain_snapshot(\n        profiler: &PoolProfiler,\n        cached_timestamp: Option<UnixNanos>,\n    ) -> anyhow::Result<UnixNanos> {\n        cached_timestamp\n            .or(profiler.last_processed_ts)\n            .context(\"missing block timestamp for on-chain snapshot\")\n    }","sourceCodeStart":2224,"sourceCodeEnd":2260,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/data/core.rs#L2224-L2260","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-fetch the RPC snapshot at the exact block hash/number used for position derivation and retry","Verify the cache's pool events (mint/burn) are fully synced up to the snapshot block before deriving tick liquidity","Check for chain reorgs and rebuild the local cache from a canonical block","Pin a consistent archive RPC endpoint (same node, pinned block) instead of a load-balanced set","Log both derived and received tick values and compare per-tick to identify which ticks diverge"],"exampleFix":"// before: deriving positions and fetching snapshot at different points in time\nlet snapshot = client.fetch_tick_snapshot(pool, latest_block()).await?;\nvalidate_rpc_snapshot_topology(&cache, &snapshot)?;\n// after: pin both to the same block\nlet block = latest_block();\nlet snapshot = client.fetch_tick_snapshot_at(pool, block).await?;\nvalidate_rpc_snapshot_topology(&cache_at_block(&cache, block), &snapshot)?;","handlingStrategy":"validation","validationCode":"let actual = rpc_snapshot.ticks.get(&tick_value).ok_or_else(|| anyhow!(\"tick {tick_value} missing\"))?;\nif actual.liquidity_gross != derived_gross || actual.liquidity_net != derived_net {\n    return Err(anyhow!(\"tick {} mismatch at block {}\", tick_value, snapshot_block));\n}","typeGuard":"fn ticks_consistent(t: &RpcTick, gross: u128, net: i128) -> bool {\n    t.liquidity_gross == gross && t.liquidity_net == net\n}","tryCatchPattern":null,"preventionTips":["Pin RPC snapshot fetches to an explicit block number/hash","Ensure event sync is complete before deriving tick topology","Use a single consistent archive node rather than load-balanced endpoints","Handle reorgs by rebuilding the cache from a canonical block"],"tags":["rpc","consistency","blockchain","liquidity","snapshot"],"backgroundTag":"internal-invariant-violation","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}