nathanmarz/storm · error · IllegalArgumentException
numBuckets must be >= 2
Error message
numBuckets must be >= 2
What it means
The TimeCacheMap constructor validates numBuckets and throws IllegalArgumentException if fewer than 2 buckets are requested. With only one bucket the expired-bucket rotation scheme cannot work (the active bucket would be cleaned immediately or never rotate), so the class forbids it at construction time.
Solutions
- Pass numBuckets >= 2, e.g. new TimeCacheMap(expirationSecs, 3, callback).
- Use the convenience constructor new TimeCacheMap(expirationSecs, callback), which defaults numBuckets to 3.
- Clamp computed values: int buckets = Math.max(2, computedBuckets); before constructing.
- Validate the config value driving numBuckets at startup and fail with a clearer message.
Example fix
// before TimeCacheMap<String, Long> cache = new TimeCacheMap<>(expiry, 1, cb); // after TimeCacheMap<String, Long> cache = new TimeCacheMap<>(expiry, Math.max(2, configuredBuckets), cb);
Defensive patterns
Strategy: validation
Validate before calling
if (buckets < 2) throw new IllegalArgumentException("TimeCacheMap requires numBuckets >= 2, got " + buckets); Try / catch
try {
cache = new TimeCacheMap<>(expirySecs, buckets, callback);
} catch (IllegalArgumentException e) {
LOG.warn("Invalid bucket count, using default 3");
cache = new TimeCacheMap<>(expirySecs, callback);
} Prevention
- Always construct with the two-arg constructor unless you have a specific reason to tune numBuckets.
- Clamp any config-derived bucket count with Math.max(2, value).
- Unit-test cache construction with boundary values (0, 1, 2).
When it happens
Trigger: Calling new TimeCacheMap<K,V>(expirationSecs, numBuckets, callback) — or the shorter constructor defaulting numBuckets — with numBuckets < 2, e.g. new TimeCacheMap(3, 1, cb) or new TimeCacheMap(3, 0, cb).
Common situations: Passing a user-supplied or config-derived bucket count that is 0 or 1; computing numBuckets from expirationSecs/bucketInterval arithmetic that rounds down to 1; misreading defaults in code copied from an older Storm version.
Related errors
- Require input fields for each aggregator
- Multireducer groupFields and inputFields must be the same…
- Each element of the list
- Field must be an Iterable of
- Field must be a power of 2.
AI-assisted analysis of nathanmarz/storm@cdb116e942 (2026-09-12).
Data as JSON: /api/errors/327a40cc380acfd1.
Report an issue: GitHub.
Appendix: source
Thrown at storm-core/src/jvm/backtype/storm/utils/TimeCacheMap.java:54
//deprecated in favor of non-threaded RotatingMap
@Deprecated
public class TimeCacheMap<K, V> {
//this default ensures things expire at most 50% past the expiration time
private static final int DEFAULT_NUM_BUCKETS = 3;
public static interface ExpiredCallback<K, V> {
public void expire(K key, V val);
}
private LinkedList<HashMap<K, V>> _buckets;
private final Object _lock = new Object();
private Thread _cleaner;
private ExpiredCallback _callback;
public TimeCacheMap(int expirationSecs, int numBuckets, ExpiredCallback<K, V> callback) {
if(numBuckets<2) {
throw new IllegalArgumentException("numBuckets must be >= 2");
}
_buckets = new LinkedList<HashMap<K, V>>();
for(int i=0; i<numBuckets; i++) {
_buckets.add(new HashMap<K, V>());
}
_callback = callback;
final long expirationMillis = expirationSecs * 1000L;
final long sleepTime = expirationMillis / (numBuckets-1);
_cleaner = new Thread(new Runnable() {
public void run() {
try {
while(true) {
Map<K, V> dead = null;
Time.sleep(sleepTime);
synchronized(_lock) {
dead = _buckets.removeLast();View on GitHub (pinned to cdb116e942)