Handle expected receive-loop cancellation without error logging/retry delay#3
Conversation
Agent-Logs-Url: https://github.com/slaveOftime/wechat-relay/sessions/f8bbb120-ea7a-4857-97b0-8e7cf20bce88 Co-authored-by: albertwoo <8017681+albertwoo@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the WeChat receive loop in WeChatService.StartReceivingAsync to treat shutdown/teardown-related cancellation as expected control flow, so the loop exits promptly without logging errors or waiting for the retry delay.
Changes:
- Replaced the
OperationCanceledException-only catch with a broader exception filter that recognizes expected cancellation during shutdown. - Added
IsExpectedReceiveLoopCancellationto detect cancellation-related exceptions (including viaInnerException), and break out of the loop immediately.
Comments suppressed due to low confidence (1)
WeChatRelay/Services/WeChatService.cs:267
await Task.Delay(..., ct)inside the genericcatch (Exception ex)can still throwOperationCanceledExceptionduring shutdown, causingStartReceivingAsyncto fault instead of exiting cleanly (the cancellation filter only applies to exceptions from thetryblock). Consider short-circuiting whenct.IsCancellationRequestedbefore delaying, or wrapping the delay in anOperationCanceledExceptioncheck that breaks the loop on cancellation.
catch (Exception ex)
{
log.LogError(ex, "Receive loop error");
await Task.Delay(TimeSpan.FromSeconds(5), ct);
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -268,6 +268,20 @@ public async Task StartReceivingAsync(Func<InboundMessage, Task> onMessage, Canc | |||
| } | |||
| } | |||
|
|
|||
| private static bool IsExpectedReceiveLoopCancellation(Exception ex, CancellationToken ct) | |||
| { | |||
| if (!ct.IsCancellationRequested) | |||
| return false; | |||
|
|
|||
| for (var current = ex; current is not null; current = current.InnerException) | |||
| { | |||
| if (current is OperationCanceledException or ObjectDisposedException) | |||
| return true; | |||
| } | |||
|
|
|||
| return false; | |||
| } | |||
|
|
|||
There was a problem hiding this comment.
No need to add a separate method, just check OperationCanceledException or TaskCancelledException should be simple and fine. Make sure the project build.
There was a problem hiding this comment.
Updated in 703cd90: removed the separate helper method and simplified the receive-loop cancellation handling to explicit TaskCanceledException / OperationCanceledException catches with cancellation-token guard. I also verified the project builds successfully with dotnet build wechat-relay.slnx (and tests pass with dotnet test wechat-relay.slnx).
Agent-Logs-Url: https://github.com/slaveOftime/wechat-relay/sessions/806538f9-ec7d-4e32-8a76-5e7026392124 Co-authored-by: albertwoo <8017681+albertwoo@users.noreply.github.com>
Receive-loop shutdown paths were treating some cancellation-related exceptions as errors, causing noisy logs and an unnecessary 5s retry delay. This updates cancellation handling so normal teardown exits the loop immediately while preserving existing error/retry behavior for real faults.
Cancellation handling in receive loop
TaskCanceledExceptionas expected shutdown flow (in addition toOperationCanceledException) when the caller token is canceled.Unexpected error path unchanged
log.LogError(ex, "Receive loop error")Task.Delay(TimeSpan.FromSeconds(5), ct)retry delayScope
WeChatRelay/Services/WeChatService.csreceive-loop logic.Original prompt
This pull request was created from Copilot chat.