{"record":{"id":"b402ea182697eb24","repo":"App-vNext/Polly","slug":"value-cannot-be-null-parameter-action-b402ea","errorCode":null,"errorMessage":"Value cannot be null. (Parameter 'action')","messagePattern":"Value cannot be null\\. \\(Parameter 'action'\\)","errorType":"exception","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"src/Polly/RateLimit/AsyncRateLimitPolicy.cs","lineNumber":24,"sourceCode":"/// </summary>\npublic class AsyncRateLimitPolicy : AsyncPolicy, IRateLimitPolicy\n{\n    private readonly IRateLimiter _rateLimiter;\n\n    internal AsyncRateLimitPolicy(IRateLimiter rateLimiter) =>\n        _rateLimiter = rateLimiter;\n\n    /// <inheritdoc/>\n    [DebuggerStepThrough]\n    protected override Task<TResult> ImplementationAsync<TResult>(\n        Func<Context, CancellationToken, Task<TResult>> action,\n        Context context,\n        CancellationToken cancellationToken,\n        bool continueOnCapturedContext)\n    {\n        if (action is null)\n        {\n            throw new ArgumentNullException(nameof(action));\n        }\n\n        return AsyncRateLimitEngine.ImplementationAsync(\n            _rateLimiter,\n            null,\n            action,\n            context,\n            continueOnCapturedContext,\n            cancellationToken);\n    }\n}\n\n/// <summary>\n/// A rate-limit policy that can be applied to asynchronous delegates returning a value of type <typeparamref name=\"TResult\"/>.\n/// </summary>\n/// <typeparam name=\"TResult\">The type of the result.</typeparam>\npublic class AsyncRateLimitPolicy<TResult> : AsyncPolicy<TResult>, IRateLimitPolicy<TResult>\n{","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/App-vNext/Polly/blob/d0e46bdb1ee11ea50d0e4b6846d2633d6bc09bac/src/Polly/RateLimit/AsyncRateLimitPolicy.cs#L6-L42","documentation":"Thrown by AsyncRateLimitPolicy.ImplementationAsync<TResult> when the action delegate is null. This is the factory-less rate limit policy (no retryAfterFactory); it guards the delegate before delegating to AsyncRateLimitEngine, consistent with all Polly strategy implementations.","triggerScenarios":"Invoking ImplementationAsync with a null action on an AsyncRateLimitPolicy instance. Normally reached via internal Policy.ExecuteAsync plumbing when a null delegate reaches the strategy, or via direct subclass/reflection use.","commonSituations":"A wrapper that conditionally passes a delegate and forwards null; a refactor that introduced a nullable action parameter; reflection-driven invocation in tests.","solutions":["Always pass a non-null Func<Context, CancellationToken, Task<TResult>> to ExecuteAsync.","If the delegate is optional in your API, branch around the call rather than passing null.","Add a precondition null check at the call site.","Enable nullable reference types to surface nullable delegates at compile time."],"exampleFix":"// before\nawait rlPolicy.ExecuteAsync<int>(null, context, ct);\n\n// after\nawait rlPolicy.ExecuteAsync<int>((ctx, ct) => Task.FromResult(42), context, ct);","handlingStrategy":"validation","validationCode":"if (action is null) throw new ArgumentNullException(nameof(action));\nawait rlPolicy.ExecuteAsync(action ?? throw new ArgumentNullException(nameof(action)), context, ct);","typeGuard":"static bool IsAsyncFunc<T>(Delegate d) => d is Func<Context, CancellationToken, Task<T>>;","tryCatchPattern":null,"preventionTips":["Never pass default(Func<...>) to ExecuteAsync.","Branch around optional delegates instead of forwarding null.","Enable NRTs to catch nullable async delegates."],"tags":["argumentnullexception","action","validation","ratelimit","legacy-api"],"backgroundTag":null,"analyzedSha":"d0e46bdb1ee11ea50d0e4b6846d2633d6bc09bac","analyzedAt":"2026-08-13T16:36:01.959Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}