apache/druid · error · IllegalStateException
Already Closed!
Error message
Already Closed!
What it means
StupidPool's ObjectResourceHolder.get() reads from an AtomicReference that is cleared to null when the holder is closed. If the reference is null, the holder was already closed and using the underlying object would be unsafe, so an IllegalStateException is thrown.
Solutions
- Keep get() within the holder's open lifetime: close only after the last get()/use completes
- Use try-with-resources around the holder to enforce ordering
- If the object is needed beyond the lease, increment/copy ownership properly or take a new object from the pool
- Check for double-close in cleanup paths (idempotent close or ownership flags)
Example fix
// before
holder.close();
T obj = holder.get(); // ISE: Already Closed!
// after
T obj;
try (ObjectResourceHolder<T> h = holder) {
obj = h.get();
use(obj);
} Defensive patterns
Strategy: try-catch
Try / catch
try { T obj = holder.get(); } catch (IllegalStateException e) { /* holder closed: take a new object from pool */ } Prevention
- Close holders only after the final get()/use
- Use try-with-resources to enforce lease ordering
- Do not cache StupidPool objects beyond their lease
When it happens
Trigger: Calling get() on a ObjectResourceHolder after close() was called on it, or on a holder obtained from a pool object whose lease was already terminated; also double-close followed by get().
Common situations: Async code retaining pool objects past their lease; closing the holder in one callback and reading the object in another; misuse of StupidPool objects as long-lived cached objects.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Already closed!
- No items available
- 08001
- A batch appenderator was already created for this peon's…
- A-Not-B requires at least 1 sketch
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/c915673cef394382.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/collections/StupidPool.java:286
final ObjectId objectId,
final Cleaners.Cleanable cleanable,
final ObjectLeakNotifier notifier
)
{
this.objectRef = new AtomicReference<>(object);
this.objectId = objectId;
this.cleanable = cleanable;
this.notifier = notifier;
}
// WARNING: it is entirely possible for a caller to hold onto the object and call ObjectResourceHolder.close,
// Then still use that object even though it will be offered to someone else in StupidPool.take
@Override
public T get()
{
final T object = objectRef.get();
if (object == null) {
throw new ISE("Already Closed!");
}
return object;
}
@Override
public void close()
{
final T object = objectRef.get();
if (object != null && objectRef.compareAndSet(object, null)) {
try {
tryReturnToPool(object, objectId, cleanable, notifier);
}
finally {
// Need to null reference to objectId because if ObjectResourceHolder is closed, but leaked, this reference
// will prevent reporting leaks of ResourceHandlers when this object and objectId are taken from the pool
// again.
objectId = null;View on GitHub (pinned to 9b90983fd2)