{"record":{"id":"f8a6168dc8ccd9dd","repo":"dotnet/aspnetcore","slug":"the-absolute-expiration-value-must-be-in-the-futur-f8a616","errorCode":null,"errorMessage":"The absolute expiration value must be in the future.","messagePattern":"The absolute expiration value must be in the future\\.","errorType":"validation","errorClass":"ArgumentOutOfRangeException","httpStatus":null,"severity":"error","filePath":"src/Caching/StackExchangeRedis/src/RedisCache.cs","lineNumber":600,"sourceCode":"                options.SlidingExpiration.Value.TotalSeconds);\n        }\n        else if (absoluteExpiration.HasValue)\n        {\n            return (long)(absoluteExpiration.Value - creationTime).TotalSeconds;\n        }\n        else if (options.SlidingExpiration.HasValue)\n        {\n            return (long)options.SlidingExpiration.Value.TotalSeconds;\n        }\n        return null;\n    }\n\n    private static DateTimeOffset? GetAbsoluteExpiration(DateTimeOffset creationTime, DistributedCacheEntryOptions options)\n    {\n        if (options.AbsoluteExpiration.HasValue && options.AbsoluteExpiration <= creationTime)\n        {\n#pragma warning disable CA2208 // Instantiate argument exceptions correctly\n            throw new ArgumentOutOfRangeException(\n                nameof(DistributedCacheEntryOptions.AbsoluteExpiration),\n                options.AbsoluteExpiration.Value,\n                \"The absolute expiration value must be in the future.\");\n#pragma warning restore CA2208 // Instantiate argument exceptions correctly\n        }\n\n        if (options.AbsoluteExpirationRelativeToNow.HasValue)\n        {\n            return creationTime + options.AbsoluteExpirationRelativeToNow;\n        }\n\n        return options.AbsoluteExpiration;\n    }\n\n    /// <inheritdoc />\n    public void Dispose()\n    {\n        if (_disposed)","sourceCodeStart":582,"sourceCodeEnd":618,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/src/Caching/StackExchangeRedis/src/RedisCache.cs#L582-L618","documentation":"Thrown by RedisCache.GetAbsoluteExpiration (line 600) during a Set/SetAsync operation when DistributedCacheEntryOptions.AbsoluteExpiration is set but its value is less than or equal to creationTime (DateTimeOffset.UtcNow). An absolute expiration at or before the current time means the item is immediately expired and is rejected. The exception type is ArgumentOutOfRangeException.","triggerScenarios":"RedisCache.SetImpl or SetImplAsync calls GetAbsoluteExpiration (line 595) with options.AbsoluteExpiration <= DateTimeOffset.UtcNow. This applies to the AbsoluteExpiration property (a fixed DateTimeOffset), not AbsoluteExpirationRelativeToNow.","commonSituations":"Clock skew between the client computing the expiration and the Redis cache host; passing a previously-computed DateTimeOffset that has since passed; incorrect UTC conversion from a local time; deserializing a cached options object whose AbsoluteExpiration is now in the past.","solutions":["Use AbsoluteExpirationRelativeToNow instead of a fixed AbsoluteExpiration to avoid clock/timezone issues: options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1).","If using AbsoluteExpiration, ensure it is a future UTC DateTimeOffset and add a small buffer for clock skew.","Add a pre-check: if (options.AbsoluteExpiration <= DateTimeOffset.UtcNow) recompute or skip caching."],"exampleFix":"// before — fixed absolute time that may be in the past by the time Set runs\nvar opts = new DistributedCacheEntryOptions\n{\n    AbsoluteExpiration = somePreviouslyComputedDateTimeOffset\n};\nawait cache.SetStringAsync(\"key\", value, opts);\n\n// after — relative duration, immune to past-time errors\nvar opts = new DistributedCacheEntryOptions\n{\n    AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30)\n};\nawait cache.SetStringAsync(\"key\", value, opts);","handlingStrategy":"validation","validationCode":"// Guard before calling Set\nif (options.AbsoluteExpiration.HasValue && options.AbsoluteExpiration.Value <= DateTimeOffset.UtcNow)\n{\n    throw new ArgumentOutOfRangeException(nameof(options.AbsoluteExpiration),\n        \"AbsoluteExpiration must be in the future.\");\n}","typeGuard":"static bool IsValidAbsoluteExpiration(DistributedCacheEntryOptions opts)\n    => !opts.AbsoluteExpiration.HasValue || opts.AbsoluteExpiration.Value > DateTimeOffset.UtcNow;","tryCatchPattern":"try\n{\n    await cache.SetAsync(key, value, options);\n}\ncatch (ArgumentOutOfRangeException ex) when (ex.Message.Contains(\"absolute expiration\"))\n{\n    options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1);\n    await cache.SetAsync(key, value, options);\n}","preventionTips":["Prefer AbsoluteExpirationRelativeToNow to sidestep clock-skew and timezone issues.","Add a pre-check ensuring AbsoluteExpiration is strictly in the future before caching."],"tags":["caching","redis","distributed-cache","configuration","expiration"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}