dotnet/aspnetcore · error · InvalidOperationException
Either absolute or sliding expiration needs to be provided.
Error message
Either absolute or sliding expiration needs to be provided.
What it means
Thrown by the SQL Server distributed cache's ValidateOptions when neither an absolute nor a sliding expiration is supplied for a cache entry. The backing stored procedure requires at least one expiration type to schedule deletion, so the cache refuses entries with no expiration policy.
Solutions
- Set at least one of AbsoluteExpiration, AbsoluteExpirationRelativeToNow, or SlidingExpiration on the options instance.
- Prefer SlidingExpiration for session-like data or AbsoluteExpirationRelativeToNow for fixed TTL.
- If you want a global default, wrap the cache and apply a default expiration before delegating to SetAsync.
Example fix
// before: no expiration
var opts = new DistributedCacheEntryOptions();
await cache.SetStringAsync(key, value, opts);
// after: provide a sliding expiration
var opts = new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(30) };
await cache.SetStringAsync(key, value, opts); Defensive patterns
Strategy: validation
Validate before calling
if (!options.AbsoluteExpiration.HasValue && !options.AbsoluteExpirationRelativeToNow.HasValue && !options.SlidingExpiration.HasValue) { throw new InvalidOperationException("Set at least one expiration."); } Type guard
static bool HasExpiration(DistributedCacheEntryOptions o) => o.AbsoluteExpiration.HasValue || o.AbsoluteExpirationRelativeToNow.HasValue || o.SlidingExpiration.HasValue;
Prevention
- Wrap IDistributedCache and apply a default expiration policy.
- Centralize cache option construction in a helper to avoid forgetting expiration.
- Add a unit test asserting every Set call supplies an expiration.
When it happens
Trigger: Calling SetAsync with a DistributedCacheEntryOptions that has neither AbsoluteExpiration, AbsoluteExpirationRelativeToNow, nor SlidingExpiration set — i.e. all three are null.
Common situations: Default-constructing DistributedCacheEntryOptions and forgetting to set any expiration; refactoring that removed the expiration line; assuming the cache provides a default lifetime (it does not at this code path).
Related errors
- ExpiredItemsDeletionInterval cannot be less than the…
- The absolute expiration value must be in the future.
- The sliding expiration value must be positive.
- The absolute expiration value must be in the future.
- ' ' is flagged with SingleDelivery, but the selected…
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/3da2af16ae708981.
Report an issue: GitHub.
Appendix: source
Thrown at src/Caching/SqlServer/src/DatabaseOperations.cs:395
absoluteExpiration = utcNow.Add(options.AbsoluteExpirationRelativeToNow.Value);
}
else if (options.AbsoluteExpiration.HasValue)
{
if (options.AbsoluteExpiration.Value <= utcNow)
{
throw new InvalidOperationException("The absolute expiration value must be in the future.");
}
absoluteExpiration = options.AbsoluteExpiration.Value;
}
return absoluteExpiration;
}
private static void ValidateOptions(TimeSpan? slidingExpiration, DateTimeOffset? absoluteExpiration)
{
if (!slidingExpiration.HasValue && !absoluteExpiration.HasValue)
{
throw new InvalidOperationException("Either absolute or sliding expiration needs " +
"to be provided.");
}
}
}
View on GitHub (pinned to 3600ca084e)