App-vNext/Polly · error · ArgumentNullException
Value cannot be null. (Parameter 'ttlStrategy')
Error message
Value cannot be null. (Parameter 'ttlStrategy')
What it means
Thrown by the terminal CacheAsync<TResult>(...) overload when ttlStrategy is null. The TTL strategy decides how long cached values live; a null strategy would leave expiration undefined, so construction is rejected. The provider guard runs first, so this only fires when the provider is valid but the TTL is null.
Solutions
- Pass a concrete ITtlStrategy<TResult> such as new RelativeTtl(TimeSpan.FromMinutes(5)).
- If you only need a fixed TTL, prefer the TimeSpan-based overload which constructs RelativeTtl for you.
- Null-check the strategy at the call site.
Example fix
// before var policy = Policy.CacheAsync<string>(provider, (ITtlStrategy<string>)null, keyFunc, onGet, onMiss, onPut, null, null); // after var policy = Policy.CacheAsync<string>(provider, new RelativeTtl(TimeSpan.FromMinutes(5)).For<string>(), keyFunc, onGet, onMiss, onPut, null, null);
Defensive patterns
Strategy: validation
Validate before calling
var ttl = ttlStrategy ?? new RelativeTtl(TimeSpan.FromMinutes(5)); var policy = Policy.CacheAsync<TResult>(provider, ttl.For<TResult>(), keyFunc, onGet, onMiss, onPut, null, null);
Type guard
static bool HasTtl<T>(ITtlStrategy<T>? s) => s is not null;
Try / catch
try { var p = Policy.CacheAsync<R>(provider, ttl, keyFunc, onGet, onMiss, onPut, null, null); }
catch (ArgumentNullException ex) when (ex.ParamName == "ttlStrategy") { /* default the TTL or fail */ } Prevention
- Default to RelativeTtl in your builder when no custom TTL is configured.
- Prefer the TimeSpan overload for fixed TTLs.
- Validate config sections that drive TTL construction.
When it happens
Trigger: Calling the full overload (or a public overload that funnels here) with a null ITtlStrategy<TResult>, e.g. passing null where a RelativeTtl or ContextualTtl should be.
Common situations: The TTL strategy field was never assigned; a config-driven TTL factory returned null when the config section was absent; refactor swapped in an overload that no longer wraps TimeSpan into RelativeTtl.
Related errors
- Value cannot be null. (Parameter 'action')
- Value cannot be null. (Parameter 'onCacheGet')
- Value cannot be null. (Parameter 'onCacheMiss')
- Value cannot be null. (Parameter 'onCachePut')
- Value cannot be null. (Parameter 'wrappedCacheProvider')
AI-assisted analysis of App-vNext/Polly@d0e46bdb1e (2026-08-13).
Data as JSON: /api/errors/057c499d0eb7028d.
Report an issue: GitHub.
Appendix: source
Thrown at src/Polly/Caching/AsyncCacheTResultSyntax.cs:986
/// <exception cref="ArgumentNullException">Thrown when <paramref name="onCachePut"/> is <see langword="null"/>.</exception>
public static AsyncCachePolicy<TResult> CacheAsync<TResult>(
IAsyncCacheProvider<TResult> cacheProvider,
ITtlStrategy<TResult> ttlStrategy,
Func<Context, string> cacheKeyStrategy,
Action<Context, string> onCacheGet,
Action<Context, string> onCacheMiss,
Action<Context, string> onCachePut,
Action<Context, string, Exception>? onCacheGetError,
Action<Context, string, Exception>? onCachePutError)
{
if (cacheProvider == null)
{
throw new ArgumentNullException(nameof(cacheProvider));
}
if (ttlStrategy == null)
{
throw new ArgumentNullException(nameof(ttlStrategy));
}
if (cacheKeyStrategy == null)
{
throw new ArgumentNullException(nameof(cacheKeyStrategy));
}
if (onCacheGet == null)
{
throw new ArgumentNullException(nameof(onCacheGet));
}
if (onCacheMiss == null)
{
throw new ArgumentNullException(nameof(onCacheMiss));
}
if (onCachePut == null)View on GitHub (pinned to d0e46bdb1e)