From ad221d368e1432998031a27ff72428749f940eac Mon Sep 17 00:00:00 2001 From: Justin Paul Date: Fri, 8 May 2026 12:29:21 -0400 Subject: [PATCH] Fix Rollback NRE: capture filename before refreshing the checkpoint list The actual restore on the service was succeeding but the GUI showed "Rollback failed: Object reference not set to an instance of an object." because RefreshAsync clears the ObservableCollection, the SelectedItem binding goes null when its source item disappears, and the next line dereferenced Selected!.FileName. Cache the filename and timestamp before any await so the post-refresh status update doesn't depend on Selected still being non-null. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../ViewModels/ConfigCheckpointsViewModel.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/WebhookServer.Gui/ViewModels/ConfigCheckpointsViewModel.cs b/src/WebhookServer.Gui/ViewModels/ConfigCheckpointsViewModel.cs index d0fa3fc..817cc68 100644 --- a/src/WebhookServer.Gui/ViewModels/ConfigCheckpointsViewModel.cs +++ b/src/WebhookServer.Gui/ViewModels/ConfigCheckpointsViewModel.cs @@ -82,8 +82,14 @@ public sealed partial class ConfigCheckpointsViewModel : ObservableObject { if (Selected is null) return; + // Capture before the refresh; the ObservableCollection.Clear() in + // RefreshAsync nulls Selected (the original instance is gone from the + // collection so the SelectedItem binding clears). + var fileName = Selected.FileName; + var savedAt = Selected.SavedAt; + var ok = MessageBox.Show( - $"Roll the configuration back to the checkpoint from {Selected.SavedAt.ToLocalTime():yyyy-MM-dd HH:mm:ss}?\n\nThe current configuration is automatically saved as a new checkpoint first, so you can roll forward again.", + $"Roll the configuration back to the checkpoint from {savedAt.ToLocalTime():yyyy-MM-dd HH:mm:ss}?\n\nThe current configuration is automatically saved as a new checkpoint first, so you can roll forward again.", "Confirm rollback", MessageBoxButton.OKCancel, MessageBoxImage.Warning); @@ -91,10 +97,10 @@ public sealed partial class ConfigCheckpointsViewModel : ObservableObject try { - await _client.RestoreBackupAsync(Selected.FileName).ConfigureAwait(false); + await _client.RestoreBackupAsync(fileName).ConfigureAwait(false); await RefreshAsync().ConfigureAwait(false); Application.Current.Dispatcher.Invoke(() => - StatusMessage = $"Rolled back to {Selected!.FileName}."); + StatusMessage = $"Rolled back to {fileName}."); } catch (Exception ex) {