{"record":{"id":"26bbe1dc7ddef500","repo":"nautechsystems/nautilus_trader","slug":"rpc-snapshot-contains-duplicate-ticks","errorCode":null,"errorMessage":"RPC snapshot contains duplicate ticks","messagePattern":"RPC snapshot contains duplicate ticks","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/data/core.rs","lineNumber":2127,"sourceCode":"\n        if actual_positions.len() != snapshot.positions.len()\n            || actual_positions != expected_positions\n        {\n            anyhow::bail!(\n                \"RPC positions do not match the complete HyperSync topology: expected {} positions, received {}\",\n                expected_positions.len(),\n                actual_positions.len()\n            );\n        }\n\n        let actual_ticks: AHashMap<_, _> = snapshot\n            .ticks\n            .iter()\n            .map(|tick| (tick.value, tick))\n            .collect();\n\n        if actual_ticks.len() != snapshot.ticks.len() {\n            anyhow::bail!(\"RPC snapshot contains duplicate ticks\");\n        }\n\n        let expected_tick_values = profiler.get_active_tick_values();\n        if actual_ticks.len() != expected_tick_values.len() {\n            anyhow::bail!(\n                \"RPC ticks do not match the complete HyperSync topology: expected {} ticks, received {}\",\n                expected_tick_values.len(),\n                actual_ticks.len()\n            );\n        }\n\n        for tick_value in expected_tick_values {\n            let expected_tick = profiler\n                .get_tick(tick_value)\n                .with_context(|| format!(\"missing replay tick {tick_value}\"))?;\n            let actual_tick = actual_ticks\n                .get(&tick_value)\n                .with_context(|| format!(\"RPC snapshot omitted tick {tick_value}\"))?;","sourceCodeStart":2109,"sourceCodeEnd":2145,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/data/core.rs#L2109-L2145","documentation":"The on-chain snapshot's ticks are collected into a map keyed by tick value; if the resulting map is smaller than the raw tick list, the RPC returned the same tick more than once. Since a Uniswap V3 tick bitmap has unique entries, duplicates indicate a malformed snapshot, so validation bails before restore.","triggerScenarios":"validate_rpc_snapshot_topology (invoked from construct_pool_profiler_from_hypersync_rpc after fetching the snapshot) receives a PoolSnapshot whose `ticks` vec contains two or more entries with the same `value`.","commonSituations":"A buggy or misbehaving RPC/contract reader aggregating ticks incorrectly; contract fetch_snapshot returning overlapping tick ranges (e.g. tick values not normalized by tick spacing); a custom fork's Multicall response parsed twice into the tick list.","solutions":["Inspect snapshot.ticks for the duplicated tick values and check the fetch_snapshot implementation that produced them (pagination/Multicall chunking may double-count a boundary tick).","Ensure the ticks requested from the pool contract are derived from the profiler's distinct active tick values (get_active_tick_values) without overlap.","Upgrade/fix the univ3_pool snapshot reader for the specific DEX fork if its tick queries return duplicated entries.","Re-run bootstrap against a different RPC endpoint to rule out endpoint-specific malformed responses."],"exampleFix":"// before\nlet requested_ticks = ticks_from_raw_query; // may contain duplicates from overlapping chunks\n// after\nlet requested_ticks: Vec<_> = requested_ticks.into_iter().map(|t| (t.value, t)).collect::<AHashMap<_,_>>().into_values().collect();","handlingStrategy":"try-catch","validationCode":"// Deduplicate/detect duplicate ticks in a snapshot before use\nlet unique: std::collections::HashSet<i32> = snapshot.ticks.iter().map(|t| t.value).collect();\nif unique.len() != snapshot.ticks.len() {\n    // snapshot is malformed; do not restore\n}","typeGuard":"fn has_unique_ticks(snapshot: &PoolSnapshot) -> bool {\n    let mut seen = std::collections::HashSet::new();\n    snapshot.ticks.iter().all(|t| seen.insert(t.value))\n}","tryCatchPattern":"match bootstrap_result {\n    Err(e) if e.to_string().contains(\"duplicate ticks\") => {\n        log::error!(\"RPC snapshot malformed (duplicate ticks): {e:#}\");\n        // re-fetch snapshot from a different endpoint or fail the bootstrap\n    }\n    other => other?,\n}","preventionTips":["Derive requested ticks from the profiler's distinct active tick values.","Fix chunked/Multicall tick fetches to avoid duplicating boundary ticks.","Validate snapshots (uniqueness + count) before restore_from_snapshot in any custom fetch path."],"tags":["validation","rpc","ticks","duplicate-data"],"backgroundTag":"unexpected-response-shape","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}