quarkusio/quarkus · error · IllegalStateException
The write-based expiration policy can only be changed if the
Error message
The write-based expiration policy can only be changed if the cache was constructed with an expire-after-write configuration value
What it means
CaffeineCacheImpl.setExpireAfterWrite only works when the cache was created with an expire-after-write value; Caffeine exposes the write-based FixedExpiration policy only if so configured. If the policy is absent, changing it is meaningless, so an IllegalStateException is thrown.
Source
Thrown at extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCacheImpl.java:301
@Override
public Set<Object> keySet() {
return Set.copyOf(cache.asMap().keySet());
}
@SuppressWarnings("unchecked")
@Override
public <V> void put(Object key, CompletableFuture<V> valueFuture) {
cache.put(key, (CompletableFuture<Object>) valueFuture);
}
@Override
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) {View on GitHub (pinned to e1c734241f)
Solutions
- Add expire-after-write to the cache config (quarkus.cache.caffeine.<name>.expire-after-write)
- Only call setExpireAfterWrite for caches known to be write-expiring (check getCacheInfo)
- Rebuild/recreate the cache with the desired policy if runtime changes are required
Example fix
// before (application.properties has no expire-after-write) cache.setExpireAfterWrite(Duration.ofSeconds(30)); // after # application.properties quarkus.cache.caffeine.myCache.expire-after-write=30S
Defensive patterns
Strategy: validation
Validate before calling
if (cache.getCacheInfo().expireAfterWrite == null) {
throw new IllegalStateException("configure expire-after-write before calling setExpireAfterWrite");
} Try / catch
try {
cache.setExpireAfterWrite(duration);
} catch (IllegalStateException e) {
log.warn("cache lacks write expiration policy; update application.properties", e);
} Prevention
- Declare expire-after-write in config for every cache whose TTL you tune at runtime
- Check CacheInfo before mutating policies
When it happens
Trigger: Calling setExpireAfterWrite (e.g. from testConfig during tests) on a cache whose Quarkus config lacks quarkus.cache.caffeine.<name>.expire-after-write.
Common situations: Tests that programmatically tune expiration without declaring the TTL in application.properties; caches configured only with maximum-size or expire-after-access.
Related errors
- The access-based expiration policy can only be changed if th
- The maximum size can only be changed if the cache was constr
- Unknown cache type:
- An existing cached value type does not match the requested t
- An existing cached value type does not match the type return
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/1c775f4711e0419e.
Report an issue: GitHub.