apache/druid · error · IllegalStateException
Closed
Error message
Closed
What it means
DirectByteBufferHolder.get returns the wrapped direct ByteBuffer; if the holder has been closed, buf was nulled and get() throws this IllegalStateException. It signals use-after-close: the caller is asking for a buffer from a resource that was already released.
Solutions
- Restructure code so all buffer use happens strictly before close().
- Keep a local ByteBuffer obtained from get() before any code path that may close the holder.
- Synchronize holder access if multiple threads can close/read concurrently.
Example fix
// before holder.close(); ByteBuffer buf = holder.get(); // ISE: Closed // after ByteBuffer buf = holder.get(); // ... use buf ... holder.close();
Defensive patterns
Strategy: try-catch
Type guard
boolean isClosed(ResourceHolder<ByteBuffer> h) { return !(h instanceof DirectByteBufferHolder) || ((DirectByteBufferHolder) h).isClosed(); } Try / catch
try {
ByteBuffer buf = holder.get();
} catch (IllegalStateException e) {
throw new IllegalStateException("buffer used after close", e);
} Prevention
- Use try-with-resources so close always runs last
- Extract the buffer via get() before any early-close paths
- Synchronize access if holders cross threads
When it happens
Trigger: Calling get() on a ResourceHolder<ByteBuffer> produced by ByteBufferUtils.allocateDirect after close() has been invoked on it, e.g. using the buffer in a finally-ordered path where close already ran, or double-consuming a holder.
Common situations: Code that closes the holder early then still dereferences the buffer; lifecycle bugs in custom serialization channels reading from a freed direct buffer; races where one thread closes while another reads.
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/6037e078ece76034.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/java/util/common/ByteBufferUtils.java:126
* like processing buffers.
*
* Holders cannot be closed more than once. Attempting to close a holder twice will earn you an
* {@link IllegalStateException}.
*/
public static ResourceHolder<ByteBuffer> allocateDirect(final int size)
{
class DirectByteBufferHolder implements ResourceHolder<ByteBuffer>
{
private final AtomicBoolean closed = new AtomicBoolean(false);
private volatile ByteBuffer buf = ByteBuffer.allocateDirect(size);
@Override
public ByteBuffer get()
{
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");
}
}
}
View on GitHub (pinned to 9b90983fd2)