{"record":{"id":"6037e078ece76034","repo":"apache/druid","slug":"closed-6037e0","errorCode":null,"errorMessage":"Closed","messagePattern":"Closed","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/java/util/common/ByteBufferUtils.java","lineNumber":126,"sourceCode":"   * like processing buffers.\n   *\n   * Holders cannot be closed more than once. Attempting to close a holder twice will earn you an\n   * {@link IllegalStateException}.\n   */\n  public static ResourceHolder<ByteBuffer> allocateDirect(final int size)\n  {\n    class DirectByteBufferHolder implements ResourceHolder<ByteBuffer>\n    {\n      private final AtomicBoolean closed = new AtomicBoolean(false);\n      private volatile ByteBuffer buf = ByteBuffer.allocateDirect(size);\n\n      @Override\n      public ByteBuffer get()\n      {\n        final ByteBuffer theBuf = buf;\n\n        if (theBuf == null) {\n          throw new ISE(\"Closed\");\n        } else {\n          return theBuf;\n        }\n      }\n\n      @Override\n      public void close()\n      {\n        if (closed.compareAndSet(false, true)) {\n          final ByteBuffer theBuf = buf;\n          buf = null;\n          free(theBuf);\n        } else {\n          throw new ISE(\"Already closed\");\n        }\n      }\n    }\n","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/java/util/common/ByteBufferUtils.java#L108-L144","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nholder.close();\nByteBuffer buf = holder.get(); // ISE: Closed\n// after\nByteBuffer buf = holder.get();\n// ... use buf ...\nholder.close();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"boolean isClosed(ResourceHolder<ByteBuffer> h) { return !(h instanceof DirectByteBufferHolder) || ((DirectByteBufferHolder) h).isClosed(); }","tryCatchPattern":"try {\n  ByteBuffer buf = holder.get();\n} catch (IllegalStateException e) {\n  throw new IllegalStateException(\"buffer used after close\", e);\n}","preventionTips":["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"],"tags":["memory","lifecycle","use-after-close"],"backgroundTag":"invalid-state-transition","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}