From d2c45ab08ceec77162c78d92e475ec554425e142 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Mon, 12 Jan 2026 14:25:36 +0100 Subject: [PATCH 1/2] Fail fast when executor ahead --- block/internal/common/replay.go | 12 ++++++------ block/internal/common/replay_test.go | 7 ++++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/block/internal/common/replay.go b/block/internal/common/replay.go index 9c95bc3f36..a9d1bf9936 100644 --- a/block/internal/common/replay.go +++ b/block/internal/common/replay.go @@ -67,15 +67,15 @@ func (s *Replayer) SyncToHeight(ctx context.Context, targetHeight uint64) error Uint64("exec_layer_height", execHeight). Msg("execution layer height check") - // If execution layer is ahead, skip syncing and continue. This can happen if execution - // progressed independently (e.g. after manual intervention). We log it for visibility but - // do not treat it as fatal. + // If execution layer is ahead, we cannot proceed safely as this indicates state divergence. + // The execution layer must be rolled back before the node can continue. if execHeight > targetHeight { - s.logger.Warn(). + s.logger.Error(). Uint64("target_height", targetHeight). Uint64("exec_layer_height", execHeight). - Msg("execution layer is ahead of target height - skipping replay") - return nil + Msg("execution layer ahead of target - manual rollback required") + return fmt.Errorf("execution layer height (%d) ahead of target height (%d): manually rollback execution layer to height %d", + execHeight, targetHeight, targetHeight) } // If execution layer is behind, sync the missing blocks diff --git a/block/internal/common/replay_test.go b/block/internal/common/replay_test.go index 6bc344d945..188e819d0c 100644 --- a/block/internal/common/replay_test.go +++ b/block/internal/common/replay_test.go @@ -135,15 +135,16 @@ func TestReplayer_SyncToHeight_ExecutorAhead(t *testing.T) { syncer := NewReplayer(mockStore, mockExec, gen, logger) - // Setup: target height is 100, execution layer is at 101 (unexpected!) + // Setup: execution layer is ahead of target (indicates state divergence) targetHeight := uint64(100) execHeight := uint64(101) mockExec.On("GetLatestHeight", mock.Anything).Return(execHeight, nil) - // Execute sync - should just log and continue without error + // Should return error to prevent proceeding with divergent state err := syncer.SyncToHeight(ctx, targetHeight) - require.NoError(t, err) + require.Error(t, err) + require.Contains(t, err.Error(), "ahead of target height") // No replay should be attempted mockExec.AssertNotCalled(t, "ExecuteTxs", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything) From a5bee2e217731bbac955786495d9a44abd242834 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Mon, 12 Jan 2026 14:28:32 +0100 Subject: [PATCH 2/2] Remove verbose error log --- block/internal/common/replay.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/block/internal/common/replay.go b/block/internal/common/replay.go index a9d1bf9936..b96567ef70 100644 --- a/block/internal/common/replay.go +++ b/block/internal/common/replay.go @@ -70,10 +70,6 @@ func (s *Replayer) SyncToHeight(ctx context.Context, targetHeight uint64) error // If execution layer is ahead, we cannot proceed safely as this indicates state divergence. // The execution layer must be rolled back before the node can continue. if execHeight > targetHeight { - s.logger.Error(). - Uint64("target_height", targetHeight). - Uint64("exec_layer_height", execHeight). - Msg("execution layer ahead of target - manual rollback required") return fmt.Errorf("execution layer height (%d) ahead of target height (%d): manually rollback execution layer to height %d", execHeight, targetHeight, targetHeight) }