Skip to content

Handle expected receive-loop cancellation without error logging/retry delay#3

Merged
albertwoo merged 3 commits into
mainfrom
copilot/update-receive-loop-cancellation-handling
May 21, 2026
Merged

Handle expected receive-loop cancellation without error logging/retry delay#3
albertwoo merged 3 commits into
mainfrom
copilot/update-receive-loop-cancellation-handling

Conversation

Copilot AI commented May 21, 2026

Copy link
Copy Markdown
Contributor

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

    • Expanded cancellation handling to also treat TaskCanceledException as expected shutdown flow (in addition to OperationCanceledException) when the caller token is canceled.
    • Expected cancellation now breaks the loop directly (no error log, no retry delay).
  • Unexpected error path unchanged

    • Non-cancellation exceptions still flow to:
      • log.LogError(ex, "Receive loop error")
      • existing Task.Delay(TimeSpan.FromSeconds(5), ct) retry delay
  • Scope

    • Change is limited to WeChatRelay/Services/WeChatService.cs receive-loop logic.
catch (TaskCanceledException) when (ct.IsCancellationRequested) { break; }
catch (OperationCanceledException) when (ct.IsCancellationRequested) { break; }
catch (Exception ex)
{
    log.LogError(ex, "Receive loop error");
    await Task.Delay(TimeSpan.FromSeconds(5), ct);
}
Original prompt

In repository slaveOftime/wechat-relay, create a pull request that updates the receive loop cancellation handling in WeChatRelay/Services/WeChatService.cs.

Current code around WeChatRelay/Services/WeChatService.cs lines 262-269 catches OperationCanceledException only when ct.IsCancellationRequested, then separately catches general Exception and logs an error followed by a 5-second delay.

The requested change is:

  • Ignore other cancellation-related exceptions as well.
  • Break out of the loop directly instead of logging an error and delaying when the exception represents normal cancellation.
  • Preserve logging and delay behavior for real unexpected errors.

Acceptance criteria:

  1. Update the exception handling so cancellation exceptions that are part of normal shutdown/teardown do not get logged as errors.
  2. The receive loop should exit promptly for those cancellation cases.
  3. Non-cancellation exceptions should still be logged with log.LogError(ex, "Receive loop error") and retain the existing retry delay behavior.
  4. Keep the change minimal and scoped to the relevant receive loop logic in WeChatRelay/Services/WeChatService.cs.

Use the existing repository conventions and ensure the PR description clearly explains why these cancellation exceptions are treated as expected control flow rather than errors.

The following is the prior conversation context from the user's chat exploration (may be truncated):

User: Create a PR to ignore other cancellation exception, to break it it directly in stead of log error and delay. Because they are just regular thing.

Assistant: I’ve started the PR creation flow and I’m waiting for your response to the confirmation dialog before I can continue.

This pull request was created from Copilot chat.

@albertwoo albertwoo marked this pull request as ready for review May 21, 2026 05:53
@albertwoo albertwoo requested a review from Copilot May 21, 2026 05:53
Copilot AI changed the title [WIP] Update receive loop cancellation handling in WeChatService Handle expected receive-loop cancellation without error logging/retry delay May 21, 2026
Copilot AI requested a review from albertwoo May 21, 2026 05:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 IsExpectedReceiveLoopCancellation to detect cancellation-related exceptions (including via InnerException), 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 generic catch (Exception ex) can still throw OperationCanceledException during shutdown, causing StartReceivingAsync to fault instead of exiting cleanly (the cancellation filter only applies to exceptions from the try block). Consider short-circuiting when ct.IsCancellationRequested before delaying, or wrapping the delay in an OperationCanceledException check 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.

Comment thread WeChatRelay/Services/WeChatService.cs Outdated
Comment on lines +262 to +284
@@ -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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to add a separate method, just check OperationCanceledException or TaskCancelledException should be simple and fine. Make sure the project build.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot AI requested a review from albertwoo May 21, 2026 06:00
@albertwoo albertwoo merged commit 4419fdd into main May 21, 2026
1 check passed
@albertwoo albertwoo deleted the copilot/update-receive-loop-cancellation-handling branch May 21, 2026 07:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants