{"record":{"id":"3922479d592dc868","repo":"App-vNext/Polly","slug":"value-cannot-be-null-parameter-oncacheget","errorCode":null,"errorMessage":"Value cannot be null. (Parameter 'onCacheGet')","messagePattern":"Value cannot be null\\. \\(Parameter 'onCacheGet'\\)","errorType":"exception","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"src/Polly/Caching/AsyncCacheTResultSyntax.cs","lineNumber":996,"sourceCode":"    {\n        if (cacheProvider == null)\n        {\n            throw new ArgumentNullException(nameof(cacheProvider));\n        }\n\n        if (ttlStrategy == null)\n        {\n            throw new ArgumentNullException(nameof(ttlStrategy));\n        }\n\n        if (cacheKeyStrategy == null)\n        {\n            throw new ArgumentNullException(nameof(cacheKeyStrategy));\n        }\n\n        if (onCacheGet == null)\n        {\n            throw new ArgumentNullException(nameof(onCacheGet));\n        }\n\n        if (onCacheMiss == null)\n        {\n            throw new ArgumentNullException(nameof(onCacheMiss));\n        }\n\n        if (onCachePut == null)\n        {\n            throw new ArgumentNullException(nameof(onCachePut));\n        }\n\n        return new AsyncCachePolicy<TResult>(cacheProvider, ttlStrategy, cacheKeyStrategy, onCacheGet, onCacheMiss, onCachePut, onCacheGetError, onCachePutError);\n    }\n\n    private static void EmptyCallback(Context context, string key)\n    {\n        // No-op","sourceCodeStart":978,"sourceCodeEnd":1014,"githubUrl":"https://github.com/App-vNext/Polly/blob/d0e46bdb1ee11ea50d0e4b6846d2633d6bc09bac/src/Polly/Caching/AsyncCacheTResultSyntax.cs#L978-L1014","documentation":"Thrown by the terminal CacheAsync<TResult>(...) overload when onCacheGet is null. This overload requires explicit lifecycle callbacks (they are not optional here); a null onCacheGet would cause a NullReferenceException at cache-hit time, so the guard rejects it at construction. Provider, TTL, and key-strategy guards run before it.","triggerScenarios":"Calling the full overload with null for the onCacheGet Action<Context,string>, or passing a callbacks object whose onCacheGet field is null.","commonSituations":"Developer assumed the callbacks were optional (they are in some overloads via EmptyCallback, but not when you call the full overload directly); refactor removed the callback assignment; test code passed null placeholders.","solutions":["Provide a non-null Action<Context,string> for onCacheGet, even if it is a no-op like (_, _) => { }.","Prefer the simpler overloads that supply EmptyCallback automatically when you do not need lifecycle hooks.","Null-check the callbacks at the call site."],"exampleFix":"// before\nvar policy = Policy.CacheAsync<string>(provider, ttlStrategy, keyFunc, null, onMiss, onPut, null, null);\n// after\nAction<Context, string> noop = (_, _) => { };\nvar policy = Policy.CacheAsync<string>(provider, ttlStrategy, keyFunc, noop, onMiss, onPut, null, null);","handlingStrategy":"validation","validationCode":"Action<Context, string> onGet = onCacheGet ?? ((_, _) => { });\nvar policy = Policy.CacheAsync<TResult>(provider, ttlStrategy, keyFunc, onGet, onMiss, onPut, null, null);","typeGuard":"static bool HasCallback(Action<Context, string>? a) => a is not null;","tryCatchPattern":"try { var p = Policy.CacheAsync<R>(provider, ttl, keyFunc, onGet, onMiss, onPut, null, null); }\ncatch (ArgumentNullException ex) when (ex.ParamName == \"onCacheGet\") { /* supply a no-op callback */ }","preventionTips":["Coalesce each callback to a no-op when not needed.","Prefer the simpler overloads that inject EmptyCallback.","Enable nullable reference types to distinguish optional vs required callbacks."],"tags":["caching","argumentnullexception","null-argument","polly-v7","cache-callback"],"backgroundTag":null,"analyzedSha":"d0e46bdb1ee11ea50d0e4b6846d2633d6bc09bac","analyzedAt":"2026-08-13T16:36:01.959Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}