App-vNext/Polly · error · ArgumentNullException

Value cannot be null. (Parameter 'ttlStrategy')

Error message

Value cannot be null. (Parameter 'ttlStrategy')

What it means

Thrown at policy-construction time by the legacy Polly v7 overload Policy.Cache(ISyncCacheProvider, ITtlStrategy, ICacheKeyStrategy, ...). The ITtlStrategy decides how long each cached item is valid; a null strategy would cause a NullReferenceException on every cache miss, so Polly validates it up front with ArgumentNullException.

Source

Thrown at src/Polly/Caching/CacheSyntax.cs:85

    /// </summary>
    /// <param name="cacheProvider">The cache provider.</param>
    /// <param name="ttlStrategy">A strategy for specifying ttl for values to be cached.</param>
    /// <param name="cacheKeyStrategy">The cache key strategy.</param>
    /// <param name="onCacheError">Delegate to call if an exception is thrown when attempting to get a value from or put a value into the cache, passing the execution context, the cache key, and the exception.</param>
    /// <returns>The policy instance.</returns>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="cacheProvider"/> is <see langword="null"/>.</exception>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="ttlStrategy"/> is <see langword="null"/>.</exception>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="cacheKeyStrategy"/> is <see langword="null"/>.</exception>
    public static CachePolicy Cache(ISyncCacheProvider cacheProvider, ITtlStrategy ttlStrategy, ICacheKeyStrategy cacheKeyStrategy, Action<Context, string, Exception>? onCacheError = null)
    {
        if (cacheProvider == null)
        {
            throw new ArgumentNullException(nameof(cacheProvider));
        }

        if (ttlStrategy == null)
        {
            throw new ArgumentNullException(nameof(ttlStrategy));
        }

        if (cacheKeyStrategy == null)
        {
            throw new ArgumentNullException(nameof(cacheKeyStrategy));
        }

        return Cache(cacheProvider, ttlStrategy, cacheKeyStrategy.GetCacheKey, EmptyCallback, EmptyCallback, EmptyCallback, onCacheError, onCacheError);
    }

    /// <summary>
    /// <para>Builds a <see cref="Policy"/> that will function like a result cache for delegate executions returning a result.</para>
    /// <para>Before executing a delegate returning a result, checks whether the <paramref name="cacheProvider" /> holds a value for the cache key determined by applying the <paramref name="cacheKeyStrategy"/> to the execution <see cref="Context"/>.
    /// If the <paramref name="cacheProvider "/> provides a value from cache, returns that value and does not execute the governed delegate.  If the <paramref name="cacheProvider" /> does not provide a value, executes the governed delegate, stores the value with the <paramref name="cacheProvider" />, then returns the value.
    /// </para>
    /// </summary>
    /// <param name="cacheProvider">The cache provider.</param>
    /// <param name="ttl">Duration (ttl) for which to cache values.</param>

View on GitHub (pinned to d0e46bdb1e)

Solutions

  1. Pass a non-null ITtlStrategy such as new RelativeTtl(ttl) or new SlidingTtl(ttl).
  2. If you only have a fixed duration, switch to the overload Policy.Cache(cacheProvider, TimeSpan ttl, cacheKeyStrategy) which wraps the TimeSpan in a RelativeTtl for you.

Example fix

// before
Policy.Cache(provider, null, myStrategy);
// after
Policy.Cache(provider, new RelativeTtl(TimeSpan.FromMinutes(5)), myStrategy);
Defensive patterns

Strategy: validation

Validate before calling

if (ttlStrategy is null) throw new InvalidOperationException("ttlStrategy must be provided.");
var policy = Policy.Cache(cacheProvider, ttlStrategy, cacheKeyStrategy);

Type guard

static bool IsTtlStrategyValid(Polly.Caching.ITtlStrategy? s) => s is not null;

Prevention

When it happens

Trigger: Calling Policy.Cache(cacheProvider, null, cacheKeyStrategy, ...) — the ITtlStrategy argument is null.

Common situations: You removed a TTL strategy but left the call site; a custom ITtlStrategy factory returned null; you intended the TimeSpan overload (which builds a RelativeTtl internally) but called the ITtlStrategy overload with null.

Related errors


AI-assisted analysis of App-vNext/Polly@d0e46bdb1e (2026-08-13). Data as JSON: /api/errors/0368dca8c707e0a9. Report an issue: GitHub.