{"record":{"id":"069779e186c387a7","repo":"nautechsystems/nautilus_trader","slug":"profiler-should-have-last-processed-event","errorCode":null,"errorMessage":"Profiler should have last_processed_event","messagePattern":"Profiler should have last_processed_event","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/data/core.rs","lineNumber":1938,"sourceCode":"    /// # Errors\n    ///\n    /// Returns an error if database operations fail when persisting the validation state.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the profiler does not have a last_processed_event when already_validated is true.\n    pub async fn check_snapshot_validity(\n        &self,\n        profiler: &PoolProfiler,\n        already_validated: bool,\n    ) -> anyhow::Result<SnapshotValidation> {\n        let (validation, block_position) = if already_validated {\n            // Skip RPC call - profiler was validated during construction from RPC\n            log::debug!(\"Snapshot already validated from RPC, skipping on-chain comparison\");\n            let last_event = profiler\n                .last_processed_event\n                .clone()\n                .expect(\"Profiler should have last_processed_event\");\n            (SnapshotValidation::OnChain, Some(last_event))\n        } else {\n            // Fetch on-chain state and compare\n            match self.get_on_chain_snapshot(profiler).await {\n                Ok(on_chain_snapshot) => {\n                    log::debug!(\"Comparing profiler state with on-chain state...\");\n                    let comparison = compare_pool_profiler_detailed(profiler, &on_chain_snapshot);\n                    let validation = if comparison.is_valid_for_snapshot() {\n                        if !comparison.is_exact_match() {\n                            log::warn!(\n                                \"Pool profiler snapshot has a non-structural mismatch (sqrt ratio, fee protocol, or protocol fees); accepting snapshot\"\n                            );\n                        }\n                        SnapshotValidation::OnChain\n                    } else {\n                        log::error!(\n                            \"Pool profiler state does NOT match on-chain smart contract state\"\n                        );","sourceCodeStart":1920,"sourceCodeEnd":1956,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/data/core.rs#L1920-L1956","documentation":"check_snapshot_validity unwraps profiler.last_processed_event with expect() when the snapshot was already validated during construction from RPC. The code assumes a profiler built from RPC state always records its last processed event; if that field is None despite already_validated being true, this line panics.","triggerScenarios":"Calling check_snapshot_validity with a PoolProfiler whose already_validated flag is true but whose last_processed_event is None — e.g. a profiler constructed or deserialized from a snapshot that skipped event tracking, passed to check_snapshot_validity from tests or RPC-triggered validation paths.","commonSituations":"Restoring a profiler from a persisted snapshot that lost the last_processed_event field (older schema version); constructing a PoolProfiler manually in tests with already_validated=true; a bug in the construction path that sets validity without setting the event.","solutions":["Ensure the PoolProfiler is constructed via the RPC path that populates last_processed_event before marking it validated.","If loading persisted snapshots, migrate/repair snapshots so last_processed_event is present, or force re-validation by setting already_validated=false.","In the library, replace expect with ok_or + error propagation so invalid profilers yield a recoverable error instead of a panic."],"exampleFix":"// before\nlet last_event = profiler.last_processed_event.clone().expect(\"Profiler should have last_processed_event\");\n// after\nlet last_event = profiler.last_processed_event.clone().ok_or_else(|| {\n    anyhow::anyhow!(\"profiler marked as validated but has no last_processed_event\")\n})?;","handlingStrategy":"validation","validationCode":"if profiler.already_validated && profiler.last_processed_event.is_none() {\n    anyhow::bail!(\"profiler claims validation but lacks last_processed_event; rebuild from RPC\");\n}\nlet (validation, block_position) = engine.check_snapshot_validity(&mut profiler).await?;","typeGuard":"fn validated_profiler_has_event(p: &PoolProfiler) -> bool {\n    !p.already_validated || p.last_processed_event.is_some()\n}","tryCatchPattern":"// Panics are not catchable as errors; validate inputs beforehand.\nmatch std::panic::catch_unwind(|| engine.check_snapshot_validity(&mut profiler)) {\n    Ok(inner) => inner?,\n    Err(_) => anyhow::bail!(\"panic validating snapshot: profiler missing last_processed_event\"),\n}","preventionTips":["Only construct PoolProfiler via the library's RPC construction path, which populates last_processed_event.","When deserializing persisted snapshots, verify last_processed_event exists (schema migration check).","Avoid setting already_validated manually in tests; run the real validation instead."],"tags":["panic","invariant-violation","snapshot-validation","rust"],"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"}