apereo/cas · warning
Unable to obtain a bucket for
Error message
Unable to obtain a bucket for [{}] What it means
DefaultBucketConsumer.consume logs this warning when the underlying bucket4j BucketStore returns null for the given key, i.e. no rate-limit bucket could be created or retrieved. The consumption attempt is abandoned and a BucketConsumptionResult with consumed=false is returned, so the request is treated as not consuming a token.
Solutions
- Verify bucket4j rate-limit configuration (bucket capacity, refill rates) is fully defined in CAS properties.
- Check the BucketStore backing (in-memory vs distributed) is correctly wired and reachable.
- Inspect the key derivation: confirm the key passed to consume() is non-null and produced as expected.
- Enable DEBUG logging for org.apereo.cas.bucket4j to trace bucket creation.
Example fix
// before: no bucket config cas.authn.throttle.bucket4j.enabled=true // after cas.authn.throttle.bucket4j.enabled=true cas.authn.throttle.bucket4j.capacity=100 cas.authn.throttle.bucket4j.refill-duration=PT1M
Defensive patterns
Strategy: fallback
Validate before calling
// before consuming, ensure rate limiting is configured
if (!casProperties.getAuthn().getThrottle().getBucket4j().isEnabled()) {
skipThrottle();
}
if (key == null || key.isBlank()) throw new IllegalArgumentException("bucket key required"); Try / catch
BucketConsumptionResult r = bucketConsumer.consume(key);
if (r == null || !r.isConsumed() && r.getTokensRemaining() < 0) {
LOGGER.warn("Bucket store returned no bucket for [{}]; allowing request via fallback", key);
return BucketConsumptionResult.builder().consumed(true).build();
} Prevention
- Define complete bucket4j capacity/refill properties before enabling throttling.
- Validate the bucket key derivation covers all requests with non-null values.
- Test bucket store creation at startup with an integration smoke test.
- Watch for repeated 'Unable to obtain a bucket' warnings as a config regression signal.
When it happens
Trigger: bucketStore.obtainBucket(key) returns null inside consume(); typically the configured bucket store cannot resolve/create a bucket for that key (store misconfiguration, empty/invalid bucket configuration, or an unsupported key).
Common situations: Rate-limiting misconfigured in cas.authn.throttle / bucket4j properties so no valid bucket definition exists; a distributed bucket store (Redis/Hazelcast) failing to materialize buckets; keys produced by a custom principal resolver not matching store expectations.
Understand the failure class
Background: "missing required config value" errors: why libraries refuse to start when a configuration key is empty, unset, or blank — this error's family across 48 libraries.
Related errors
- The request is throttled as capacity is entirely consumed…
- Validation attempt for principal is throttled
- Throttled submission
- Throttling submission from
- Authentication throttling rate
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/4aa298d6d01776b0.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-bucket4j-core/src/main/java/org/apereo/cas/bucket4j/consumer/DefaultBucketConsumer.java:34
*
* @author Misagh Moayyed
* @since 6.5.0
*/
@RequiredArgsConstructor
@Slf4j
public class DefaultBucketConsumer implements BucketConsumer {
private final CasReentrantLock lock = new CasReentrantLock();
private final BucketStore bucketStore;
private final BaseBucket4jProperties properties;
@Override
public BucketConsumptionResult consume(final String key) {
return lock.tryLock(() -> {
val bucket = bucketStore.obtainBucket(key);
if (bucket == null) {
LOGGER.warn("Unable to obtain a bucket for [{}]", key);
return BucketConsumptionResult.builder().consumed(false).build();
}
val canProceed = FunctionUtils.doAndHandle(() -> {
if (properties.isBlocking()) {
LOGGER.debug("Attempting to consume a token for the authentication attempt");
return bucket.tryConsume(1, MAX_WAIT_NANOS, BlockingStrategy.PARKING);
}
return bucket.tryConsume(1);
}, e -> {
LoggingUtils.error(LOGGER, e);
Thread.currentThread().interrupt();
return false;
}).get();
val headers = new LinkedHashMap<String, String>();
val availableTokens = bucket.getAvailableTokens();
if (!canProceed) {View on GitHub (pinned to e7288fc434)