{"record":{"id":"d14489de0cb8fd5e","repo":"App-vNext/Polly","slug":"value-cannot-be-null-parameter-action","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/Caching/CachePolicy.cs","lineNumber":46,"sourceCode":"    {\n        _syncCacheProvider = syncCacheProvider;\n        _ttlStrategy = ttlStrategy;\n        _cacheKeyStrategy = cacheKeyStrategy;\n\n        _onCacheGet = onCacheGet;\n        _onCachePut = onCachePut;\n        _onCacheMiss = onCacheMiss;\n        _onCacheGetError = onCacheGetError;\n        _onCachePutError = onCachePutError;\n    }\n\n    /// <inheritdoc/>\n    protected override void Implementation(Action<Context, CancellationToken> action, Context context, CancellationToken cancellationToken)\n    {\n        // Pass-through/NOOP policy action, for void-returning calls through a cache policy.\n        if (action is null)\n        {\n            throw new ArgumentNullException(nameof(action));\n        }\n\n        action(context, cancellationToken);\n    }\n\n    /// <inheritdoc/>\n    [DebuggerStepThrough]\n    protected override TResult Implementation<TResult>(Func<Context, CancellationToken, TResult> action, Context context, CancellationToken cancellationToken)\n    {\n        if (action is null)\n        {\n            throw new ArgumentNullException(nameof(action));\n        }\n\n        return CacheEngine.Implementation(\n            _syncCacheProvider.For<TResult>(),\n            _ttlStrategy.For<TResult>(),\n            _cacheKeyStrategy,","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/App-vNext/Polly/blob/d0e46bdb1ee11ea50d0e4b6846d2633d6bc09bac/src/Polly/Caching/CachePolicy.cs#L28-L64","documentation":"Thrown by CachePolicy.Implementation(Action<Context,CancellationToken>, ...) when the action is null. This override handles void-returning delegate executions through a cache policy as a pass-through/NOOP (caching only applies to results), but it still requires a non-null action to execute. The guard prevents a NullReferenceException when invoking the delegate.","triggerScenarios":"Executing a void-returning delegate through a CachePolicy and passing null for the action, e.g. policy.Execute((Action<Context,CancellationToken>)null, context, ct). More commonly reached when a wrapper/adapter forwards a null delegate.","commonSituations":"A code path conditionally supplies a delegate and the null branch was not handled; an adapter layer passes through a null action; tests invoke the policy with a null action by mistake.","solutions":["Pass a non-null Action to policy.Execute.","If the delegate is optional, branch before calling Execute rather than passing null.","Null-check the action before invoking the policy."],"exampleFix":"// before\npolicy.Execute((Action<Context, CancellationToken>)null, context, ct);\n// after\npolicy.Execute((ctx, token) => DoWork(ctx, token), context, ct);","handlingStrategy":"validation","validationCode":"if (action is null) throw new InvalidOperationException(\"Cannot execute a null action through the cache policy.\");\npolicy.Execute(action, context, cancellationToken);","typeGuard":"static bool HasAction(Action<Context, CancellationToken>? a) => a is not null;","tryCatchPattern":"try { policy.Execute(action, context, ct); }\ncatch (ArgumentNullException ex) when (ex.ParamName == \"action\") { /* supply a real action or skip execution */ }","preventionTips":["Branch around policy.Execute when the delegate is optional.","Null-check delegates at the call site.","Avoid adapter layers that forward null delegates."],"tags":["caching","argumentnullexception","null-argument","polly-v7","policy-execution"],"backgroundTag":null,"analyzedSha":"d0e46bdb1ee11ea50d0e4b6846d2633d6bc09bac","analyzedAt":"2026-08-13T16:36:01.959Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}