diff --git a/src/Polly/Retry/AsyncRetryPolicy.cs b/src/Polly/Retry/AsyncRetryPolicy.cs
index e8ce73c90c8..1991941df1c 100644
--- a/src/Polly/Retry/AsyncRetryPolicy.cs
+++ b/src/Polly/Retry/AsyncRetryPolicy.cs
@@ -3,7 +3,6 @@
///
/// A retry policy that can be applied to asynchronous delegates.
///
-#pragma warning disable CA1062 // Validate arguments of public methods
public class AsyncRetryPolicy : AsyncPolicy, IRetryPolicy
{
private readonly Func _onRetryAsync;
@@ -34,6 +33,11 @@ protected override Task ImplementationAsync(
CancellationToken cancellationToken,
bool continueOnCapturedContext)
{
+ if (action is null)
+ {
+ throw new ArgumentNullException(nameof(action));
+ }
+
var sleepDurationProvider = _sleepDurationProvider != null
? (retryCount, outcome, ctx) => _sleepDurationProvider(retryCount, outcome.Exception, ctx)
: (Func, Context, TimeSpan>)null;
@@ -79,9 +83,18 @@ internal AsyncRetryPolicy(
///
[DebuggerStepThrough]
- protected override Task ImplementationAsync(Func> action, Context context, CancellationToken cancellationToken,
- bool continueOnCapturedContext) =>
- AsyncRetryEngine.ImplementationAsync(
+ protected override Task ImplementationAsync(
+ Func> action,
+ Context context,
+ CancellationToken cancellationToken,
+ bool continueOnCapturedContext)
+ {
+ if (action is null)
+ {
+ throw new ArgumentNullException(nameof(action));
+ }
+
+ return AsyncRetryEngine.ImplementationAsync(
action,
context,
ExceptionPredicates,
@@ -92,5 +105,6 @@ protected override Task ImplementationAsync(Func> action = null!;
+ var policyBuilder = new PolicyBuilder(exception => exception);
+ Func onRetryAsync = (_, _, _, _) => Task.CompletedTask;
+ int permittedRetryCount = int.MaxValue;
+ IEnumerable? sleepDurationsEnumerable = null;
+ Func sleepDurationProvider = null!;
+
+ var instance = Activator.CreateInstance(
+ typeof(AsyncRetryPolicy),
+ flags,
+ null,
+ [policyBuilder, onRetryAsync, permittedRetryCount, sleepDurationsEnumerable, sleepDurationProvider],
+ null)!;
+ var instanceType = instance.GetType();
+ var methods = instanceType.GetMethods(flags);
+ var methodInfo = methods.First(method => method is { Name: "ImplementationAsync", ReturnType.Name: "Task`1" });
+ var generic = methodInfo.MakeGenericMethod(typeof(EmptyStruct));
+
+ var func = () => generic.Invoke(instance, [action, new Context(), CancellationToken.None, false]);
+
+ var exceptionAssertions = func.Should().Throw();
+ exceptionAssertions.And.Message.Should().Be("Exception has been thrown by the target of an invocation.");
+ exceptionAssertions.And.InnerException.Should().BeOfType()
+ .Which.ParamName.Should().Be("action");
+ }
+
[Fact]
public void Should_throw_when_retry_count_is_less_than_zero_without_context()
{
diff --git a/test/Polly.Specs/Retry/RetryTResultSpecsAsync.cs b/test/Polly.Specs/Retry/RetryTResultSpecsAsync.cs
index 9ebeb0948a2..cd3b9e5ef2a 100644
--- a/test/Polly.Specs/Retry/RetryTResultSpecsAsync.cs
+++ b/test/Polly.Specs/Retry/RetryTResultSpecsAsync.cs
@@ -4,6 +4,35 @@ namespace Polly.Specs.Retry;
public class RetryTResultSpecsAsync
{
+ [Fact]
+ public void Should_throw_when_action_is_null()
+ {
+ var flags = BindingFlags.NonPublic | BindingFlags.Instance;
+ Func> action = null!;
+ var policyBuilder = new PolicyBuilder(exception => exception);
+ Func, TimeSpan, int, Context, Task> onRetryAsync = (_, _, _, _) => Task.CompletedTask;
+ int permittedRetryCount = int.MaxValue;
+ IEnumerable? sleepDurationsEnumerable = null;
+ Func, Context, TimeSpan> sleepDurationProvider = null!;
+
+ var instance = Activator.CreateInstance(
+ typeof(AsyncRetryPolicy),
+ flags,
+ null,
+ [policyBuilder, onRetryAsync, permittedRetryCount, sleepDurationsEnumerable, sleepDurationProvider],
+ null)!;
+ var instanceType = instance.GetType();
+ var methods = instanceType.GetMethods(flags);
+ var methodInfo = methods.First(method => method is { Name: "ImplementationAsync", ReturnType.Name: "Task`1" });
+
+ var func = () => methodInfo.Invoke(instance, [action, new Context(), CancellationToken.None, false]);
+
+ var exceptionAssertions = func.Should().Throw();
+ exceptionAssertions.And.Message.Should().Be("Exception has been thrown by the target of an invocation.");
+ exceptionAssertions.And.InnerException.Should().BeOfType()
+ .Which.ParamName.Should().Be("action");
+ }
+
[Fact]
public void Should_throw_when_retry_count_is_less_than_zero_without_context()
{