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

  1. Restructure code so all buffer use happens strictly before close().
  2. Keep a local ByteBuffer obtained from get() before any code path that may close the holder.
  3. 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

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)