{"record":{"id":"6f592e2d17cf2f35","repo":"apache/druid","slug":"leaks-happened-each-suppressed-exception-represen","errorCode":null,"errorMessage":"Leaks happened, each suppressed exception represents one code path that checked out an object and didn't return it.","messagePattern":"Leaks happened, each suppressed exception represents one code path that checked out an object and didn't return it\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"critical","filePath":"processing/src/main/java/org/apache/druid/collections/StupidPool.java","lineNumber":154,"sourceCode":"  }\n\n  @Override\n  public String toString()\n  {\n    return \"StupidPool{\" +\n           \"name=\" + name +\n           \", objectsCacheMaxCount=\" + objectsCacheMaxCount +\n           \", poolSize=\" + poolSize() +\n           \"}\";\n  }\n\n  @Override\n  public ResourceHolder<T> take()\n  {\n    ObjectResourceHolder resourceHolder = objects.poll();\n    if (resourceHolder == null) {\n      if (POISONED.get() && capturedException.get() != null) {\n        throw makeExceptionForLeaks(capturedException.get());\n      }\n      return makeObjectWithHandler();\n    } else {\n      poolSize.decrementAndGet();\n      if (POISONED.get()) {\n        final CopyOnWriteArrayList<LeakedException> exceptionList = capturedException.get();\n        if (exceptionList == null) {\n          resourceHolder.notifier.except = new LeakedException(Thread.currentThread().getName());\n        } else {\n          throw makeExceptionForLeaks(exceptionList);\n        }\n      }\n      return resourceHolder;\n    }\n  }\n\n  private RuntimeException makeExceptionForLeaks(CopyOnWriteArrayList<LeakedException> exceptionList)\n  {","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/collections/StupidPool.java#L136-L172","documentation":"StupidPool.take throws this when the pool has been 'poisoned' by a previously detected leak (a checked-out object's holder was garbage-collected without close/return) and the recorded leak list is present. Each suppressed LeakedException carries the stack trace of a code path that checked out an object (typically an off-heap ByteBuffer) and never returned it. This is a resource-leak detection mechanism, not a transient failure.","triggerScenarios":"Pool is poisoned by a leak notification (Cleaner detected an unclosed ObjectResourceHolder); a later take() finds no pooled object and capturedException is non-null, so makeExceptionForLeaks throws with all leak stack traces as suppressed exceptions.","commonSituations":"Query processing code paths that allocate merge buffers/column caches and exit early on exceptions without closing holders; forgotten close() in custom operator or extension code; holder dropped without try-with-resources.","solutions":["Inspect the suppressed LeakedException stack traces to identify the code path that leaked the object.","Ensure every ResourceHolder from take() is used in try-with-resources or explicitly closed in a finally block.","Fix the leaky code path (Druid or extension code) that dropped the holder without returning it.","Restart/retry after the leak fix — the pool stays poisoned, so the error repeats until the process is restarted."],"exampleFix":"// before\nResourceHolder<ByteBuffer> holder = pool.take();\nByteBuffer buf = holder.get();\nprocess(buf); // may throw; holder never closed\n// after\ntry (ResourceHolder<ByteBuffer> holder = pool.take()) {\n  process(holder.get());\n}","handlingStrategy":"try-catch","validationCode":"if (pool.poolSize() < 0 || pool.leakedObjectsCount() > 0) { log.warn(\"pool has prior leaks; holders may not be closed somewhere\"); }","typeGuard":null,"tryCatchPattern":"try (ResourceHolder<T> holder = pool.take()) {\n  use(holder.get());\n} catch (RuntimeException e) {\n  if (e.getMessage() != null && e.getMessage().startsWith(\"Leaks happened\")) {\n    for (Throwable suppressed : e.getSuppressed()) { logLeakSite(suppressed); }\n  }\n  throw e;\n}","preventionTips":["Always use try-with-resources for ResourceHolder objects taken from StupidPool.","Never let exceptions escape between take() and close().","Enable leak-detection early in tests so leak sites surface before production.","Restart the process to clear the poisoned pool only after identifying the leak."],"tags":["memory-leak","resource-pool","bytebuffer","suppressed-exceptions"],"backgroundTag":"resource-leak-detected","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"}