nautechsystems/nautilus_trader · error
Profiler should have last_processed_event
Error message
Profiler should have last_processed_event
What it means
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.
Source
Thrown at crates/adapters/blockchain/src/data/core.rs:1938
/// # Errors
///
/// Returns an error if database operations fail when persisting the validation state.
///
/// # Panics
///
/// Panics if the profiler does not have a last_processed_event when already_validated is true.
pub async fn check_snapshot_validity(
&self,
profiler: &PoolProfiler,
already_validated: bool,
) -> anyhow::Result<SnapshotValidation> {
let (validation, block_position) = if already_validated {
// Skip RPC call - profiler was validated during construction from RPC
log::debug!("Snapshot already validated from RPC, skipping on-chain comparison");
let last_event = profiler
.last_processed_event
.clone()
.expect("Profiler should have last_processed_event");
(SnapshotValidation::OnChain, Some(last_event))
} else {
// Fetch on-chain state and compare
match self.get_on_chain_snapshot(profiler).await {
Ok(on_chain_snapshot) => {
log::debug!("Comparing profiler state with on-chain state...");
let comparison = compare_pool_profiler_detailed(profiler, &on_chain_snapshot);
let validation = if comparison.is_valid_for_snapshot() {
if !comparison.is_exact_match() {
log::warn!(
"Pool profiler snapshot has a non-structural mismatch (sqrt ratio, fee protocol, or protocol fees); accepting snapshot"
);
}
SnapshotValidation::OnChain
} else {
log::error!(
"Pool profiler state does NOT match on-chain smart contract state"
);View on GitHub (pinned to 18893faf8b)
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.
Example fix
// before
let last_event = profiler.last_processed_event.clone().expect("Profiler should have last_processed_event");
// after
let last_event = profiler.last_processed_event.clone().ok_or_else(|| {
anyhow::anyhow!("profiler marked as validated but has no last_processed_event")
})?; Defensive patterns
Strategy: validation
Validate before calling
if profiler.already_validated && profiler.last_processed_event.is_none() {
anyhow::bail!("profiler claims validation but lacks last_processed_event; rebuild from RPC");
}
let (validation, block_position) = engine.check_snapshot_validity(&mut profiler).await?; Type guard
fn validated_profiler_has_event(p: &PoolProfiler) -> bool {
!p.already_validated || p.last_processed_event.is_some()
} Try / catch
// Panics are not catchable as errors; validate inputs beforehand.
match std::panic::catch_unwind(|| engine.check_snapshot_validity(&mut profiler)) {
Ok(inner) => inner?,
Err(_) => anyhow::bail!("panic validating snapshot: profiler missing last_processed_event"),
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
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
- database presence is checked by caller
- in-flight mutex poisoned
- wallet balance mutex poisoned
- instrument update lock poisoned
- rate limiter decision lock poisoned
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/069779e186c387a7.
Report an issue: GitHub.