From ce37428e1eabde7edba4b759e1de665205093f44 Mon Sep 17 00:00:00 2001 From: erhant Date: Fri, 10 Jul 2026 18:13:21 +0300 Subject: [PATCH] fix(indexer): handle `park_undeserializable` errors properly --- lez/indexer/core/src/lib.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lez/indexer/core/src/lib.rs b/lez/indexer/core/src/lib.rs index fe5aec11..719caa08 100644 --- a/lez/indexer/core/src/lib.rs +++ b/lez/indexer/core/src/lib.rs @@ -146,7 +146,10 @@ impl IndexerCore { /// Parks on an inscription that could not be parsed as an L2 block: /// records the stall and flips the status. The validated tip stays frozen. - fn park_undeserializable(&self, slot: Slot, error: std::io::Error) { + /// + /// Returns `false` if the stall could not be recorded durably; the caller + /// must then hold the cursor and retry instead of advancing past the slot. + fn park_undeserializable(&self, slot: Slot, error: std::io::Error) -> bool { let error = anyhow::Error::new(error); // use `:#` to get the entire error chain @@ -156,11 +159,14 @@ impl IndexerCore { self.store .record_stall(None, slot, BlockIngestError::Deserialize(reason.clone())) { - warn!("Failed to record stall reason: {err:#}"); + error!("Failed to record stall reason: {err:#}"); + self.set_status(IndexerSyncStatus::error(format!("store error: {err:#}"))); + return false; } self.set_status(IndexerSyncStatus::stalled(format!( "failed to deserialize L2 block: {reason}" ))); + true } pub fn subscribe_parse_block_stream(&self) -> impl futures::Stream> + '_ { @@ -212,7 +218,11 @@ impl IndexerCore { let block: Block = match borsh::from_slice(&zone_block.data) { Ok(b) => b, Err(error) => { - self.park_undeserializable(slot, error); + // The stall must be durable before the cursor moves. + if !self.park_undeserializable(slot, error) { + had_cycle_error = true; + break; + } // L1 proceeds regardless self.advance_cursor(&mut cursor, slot); continue;