quarkusio/quarkus · error · IllegalStateException

The access-based expiration policy can only be changed if th

Error message

The access-based expiration policy can only be changed if the cache was constructed with an expire-after-access configuration value

What it means

CaffeineCacheImpl.setExpireAfterAccess requires that the cache was constructed with an expire-after-access configuration value, since Caffeine only exposes the access-based FixedExpiration policy in that case. Otherwise an IllegalStateException is thrown.

Source

Thrown at extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCacheImpl.java:313

    public void setExpireAfterWrite(Duration duration) {
        Optional<FixedExpiration<Object, Object>> fixedExpiration = cache.synchronous().policy().expireAfterWrite();
        if (fixedExpiration.isPresent()) {
            fixedExpiration.get().setExpiresAfter(duration);
            cacheInfo.expireAfterWrite = duration;
        } else {
            throw new IllegalStateException("The write-based expiration policy can only be changed if the cache was " +
                    "constructed with an expire-after-write configuration value");
        }
    }

    @Override
    public void setExpireAfterAccess(Duration duration) {
        Optional<FixedExpiration<Object, Object>> fixedExpiration = cache.synchronous().policy().expireAfterAccess();
        if (fixedExpiration.isPresent()) {
            fixedExpiration.get().setExpiresAfter(duration);
            cacheInfo.expireAfterAccess = duration;
        } else {
            throw new IllegalStateException("The access-based expiration policy can only be changed if the cache was " +
                    "constructed with an expire-after-access configuration value");
        }
    }

    @Override
    public void setMaximumSize(long maximumSize) {
        Optional<Policy.Eviction<Object, Object>> eviction = cache.synchronous().policy().eviction();
        if (eviction.isPresent()) {
            eviction.get().setMaximum(maximumSize);
            cacheInfo.maximumSize = maximumSize;
        } else {
            throw new IllegalStateException("The maximum size can only be changed if the cache was constructed with a " +
                    "maximum-size configuration value");
        }
    }

    // For testing purposes only.
    public CaffeineCacheInfo getCacheInfo() {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Set quarkus.cache.caffeine.<name>.expire-after-access in configuration
  2. Guard the call by checking the policy is present before mutating
  3. Recreate the cache with the desired configuration

Example fix

// before
# no expire-after-access configured
cache.setExpireAfterAccess(Duration.ofMinutes(5));
// after
# application.properties
quarkus.cache.caffeine.myCache.expire-after-access=5M
Defensive patterns

Strategy: validation

Validate before calling

if (cache.getCacheInfo().expireAfterAccess == null) {
    throw new IllegalStateException("configure expire-after-access before calling setExpireAfterAccess");
}

Try / catch

try {
    cache.setExpireAfterAccess(duration);
} catch (IllegalStateException e) {
    log.warn("cache lacks access expiration policy; update application.properties", e);
}

Prevention

When it happens

Trigger: Calling setExpireAfterAccess (e.g. from testConfig) on a cache configured without quarkus.cache.caffeine.<name>.expire-after-access.

Common situations: Attempting to enable access-based expiry at runtime on a cache configured with only expire-after-write or maximum-size.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/36b4dd33db46776d. Report an issue: GitHub.