{"record":{"id":"e57cbc75640d5ff8","repo":"apache/druid","slug":"already-closed-e57cbc","errorCode":null,"errorMessage":"Already closed","messagePattern":"Already closed","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/java/util/common/ByteBufferUtils.java","lineNumber":140,"sourceCode":"      {\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\n    return new DirectByteBufferHolder();\n  }\n\n  /**\n   * Releases memory held by the given direct ByteBuffer\n   *\n   * @param buffer buffer to free\n   */\n  public static void free(ByteBuffer buffer)\n  {\n    if (buffer.isDirect()) {\n      clean(buffer);\n    }\n  }","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/java/util/common/ByteBufferUtils.java#L122-L158","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ntry (ResourceHolder<ByteBuffer> holder = ByteBufferUtils.allocateDirect(1024)) {\n  holder.close(); // double close on scope exit\n}\n// after\ntry (ResourceHolder<ByteBuffer> holder = ByteBufferUtils.allocateDirect(1024)) {\n  // use holder only\n}","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"// if idempotent close is desired:\ntry {\n  holder.close();\n} catch (IllegalStateException e) {\n  // already closed; ignore\n}","preventionTips":["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"],"tags":["memory","lifecycle","double-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"}