From b189ccc2f50e154eb8bbd1a9e3670d628eb1354c Mon Sep 17 00:00:00 2001 From: Gordon Beeming Date: Thu, 26 Mar 2026 16:30:10 +1000 Subject: [PATCH] Address PR #10 review feedback - Catch OperationCanceledException explicitly in the SignalR reconnection loop so shutdown cancellation logs cleanly instead of appearing as a reconnection failure with noisy backoff warnings. - Remove Process.Dispose() from HealthCheckAsync crash detection to avoid racing with SpawnProcessAsync/MonitorAdoptedProcessAsync that may still hold the Process handle. The owning code path remains responsible for disposal; the 1-hour cleanup catches any stragglers. Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: GitButler --- src/ClaudeNest.Agent/Services/SessionManager.cs | 7 ++++--- src/ClaudeNest.Agent/Services/SignalRConnectionManager.cs | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/ClaudeNest.Agent/Services/SessionManager.cs b/src/ClaudeNest.Agent/Services/SessionManager.cs index b90a5d0e..72bdf881 100644 --- a/src/ClaudeNest.Agent/Services/SessionManager.cs +++ b/src/ClaudeNest.Agent/Services/SessionManager.cs @@ -200,11 +200,12 @@ public async Task HealthCheckAsync() } catch { - // Process no longer exists — dispose handle immediately to free native resources + // Process no longer exists — mark session as crashed and clear process reference. + // Don't dispose Process here as SpawnProcessAsync/MonitorAdoptedProcessAsync may + // still be awaiting WaitForExitAsync() on the same instance. Let the owning code + // path handle disposal; the 1-hour cleanup will catch any stragglers. session.State = SessionState.Crashed; session.EndedAt = DateTime.UtcNow; - session.Process?.Dispose(); - session.Process = null; await NotifyStatusChangedAsync(session); } } diff --git a/src/ClaudeNest.Agent/Services/SignalRConnectionManager.cs b/src/ClaudeNest.Agent/Services/SignalRConnectionManager.cs index 64b73d07..ec523819 100644 --- a/src/ClaudeNest.Agent/Services/SignalRConnectionManager.cs +++ b/src/ClaudeNest.Agent/Services/SignalRConnectionManager.cs @@ -118,6 +118,11 @@ public async Task ConnectAsync(CancellationToken stoppingToken) await OnReconnected(_connection.ConnectionId); break; } + catch (OperationCanceledException) when (_stoppingToken.IsCancellationRequested) + { + _logger.LogInformation("Reconnection cancelled — agent is shutting down"); + break; + } catch (Exception ex) { _logger.LogWarning(ex, "Manual reconnection failed, retrying in {Delay}s...", delay.TotalSeconds);