apache/druid · error · IllegalStateException
Already closed
Error message
Already closed
What it means
DirectByteBufferHolder.close frees the direct ByteBuffer exactly once, guarded by a CAS on `closed`. If close() is invoked a second time, the CAS fails and it throws this IllegalStateException rather than silently double-freeing. It is a defensive check against double-close.
Solutions
- Remove redundant close() calls so the holder is closed exactly once, preferring try-with-resources.
- Track closed state on your side if the holder may pass through multiple owners.
- If double-close may legitimately occur in your code, catch the ISE and treat it as idempotent-close.
Example fix
// before
try (ResourceHolder<ByteBuffer> holder = ByteBufferUtils.allocateDirect(1024)) {
holder.close(); // double close on scope exit
}
// after
try (ResourceHolder<ByteBuffer> holder = ByteBufferUtils.allocateDirect(1024)) {
// use holder only
} Defensive patterns
Strategy: validation
Try / catch
// if idempotent close is desired:
try {
holder.close();
} catch (IllegalStateException e) {
// already closed; ignore
} Prevention
- Close each holder exactly once via try-with-resources
- Don't mix explicit close() with managed scopes
- Audit cleanup paths after refactors to remove duplicate closes
When it happens
Trigger: Calling close() twice on the same DirectByteBufferHolder, e.g. in both a try-with-resources and an explicit finally, or nested resource-management wrappers closing the same holder.
Common situations: Duplicated cleanup code paths (error path plus normal path both closing); wrapping an already-managed holder in another AutoCloseable; refactoring that left an old close call in place.
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
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/e57cbc75640d5ff8.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/java/util/common/ByteBufferUtils.java:140
{
final ByteBuffer theBuf = buf;
if (theBuf == null) {
throw new ISE("Closed");
} else {
return theBuf;
}
}
@Override
public void close()
{
if (closed.compareAndSet(false, true)) {
final ByteBuffer theBuf = buf;
buf = null;
free(theBuf);
} else {
throw new ISE("Already closed");
}
}
}
return new DirectByteBufferHolder();
}
/**
* Releases memory held by the given direct ByteBuffer
*
* @param buffer buffer to free
*/
public static void free(ByteBuffer buffer)
{
if (buffer.isDirect()) {
clean(buffer);
}
}View on GitHub (pinned to 9b90983fd2)