What's wrong
If the user's Action throws, the next tick's TryRun() rethrows that exception inside the polling loop's async lambda (IntervalAction/IntervalAction.cs, ~lines 221–240). When an async method ends with an OperationCanceledException, its task becomes Canceled, not Faulted. TaskCanceledException derives from OperationCanceledException, and HttpClient throws it on a timeout, so this is not a rare case.
Once that happens, PollingTask.Status == Canceled and PollingTask.Exception == null. RethrowExceptions() (line ~299) only looks at PollingTask.Exception, so it returns normally. The loop has stopped for good, but ShouldPoll still reads true and nothing reports the failure.
Repro
PollingInterval = 20ms, ActionInterval = 0, Action = () => throw new TaskCanceledException("http timeout"). After 500 ms:
- the action has run once
- the polling task is
Canceled
RethrowExceptions() throws nothing
For comparison, the same setup throwing InvalidOperationException leaves the task Faulted, and RethrowExceptions() throws as documented. Both results were confirmed with a temporary MSTest against main (f6424e3).
Suggested fix
Take either approach:
- In
RethrowExceptions(), also handle PollingTask.IsCanceled and rethrow the action's original exception. The faulted ActionTask still holds it: ExceptionDispatchInfo.Capture(ActionTask.Exception!.GetBaseException()).Throw().
- Or stop an action failure from surfacing as cancellation. Have the loop catch the
OperationCanceledException that TryRun rethrows and turn it into a fault, so PollingTask ends up Faulted.
Acceptance: add a regression test in which the action throws TaskCanceledException and RethrowExceptions() throws it.
What's wrong
If the user's
Actionthrows, the next tick'sTryRun()rethrows that exception inside the polling loop'sasynclambda (IntervalAction/IntervalAction.cs, ~lines 221–240). When an async method ends with anOperationCanceledException, its task becomes Canceled, not Faulted.TaskCanceledExceptionderives fromOperationCanceledException, andHttpClientthrows it on a timeout, so this is not a rare case.Once that happens,
PollingTask.Status == CanceledandPollingTask.Exception == null.RethrowExceptions()(line ~299) only looks atPollingTask.Exception, so it returns normally. The loop has stopped for good, butShouldPollstill readstrueand nothing reports the failure.Repro
PollingInterval = 20ms,ActionInterval = 0,Action = () => throw new TaskCanceledException("http timeout"). After 500 ms:CanceledRethrowExceptions()throws nothingFor comparison, the same setup throwing
InvalidOperationExceptionleaves the taskFaulted, andRethrowExceptions()throws as documented. Both results were confirmed with a temporary MSTest againstmain(f6424e3).Suggested fix
Take either approach:
RethrowExceptions(), also handlePollingTask.IsCanceledand rethrow the action's original exception. The faultedActionTaskstill holds it:ExceptionDispatchInfo.Capture(ActionTask.Exception!.GetBaseException()).Throw().OperationCanceledExceptionthatTryRunrethrows and turn it into a fault, soPollingTaskends up Faulted.Acceptance: add a regression test in which the action throws
TaskCanceledExceptionandRethrowExceptions()throws it.