{"record":{"id":"ea520ce7fdf70738","repo":"apache/druid","slug":"already-closed","errorCode":null,"errorMessage":"Already closed","messagePattern":"Already closed","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/collections/CloseableResourceHolder.java","lineNumber":46,"sourceCode":"\npublic class CloseableResourceHolder<T extends Closeable> implements ResourceHolder<T>\n{\n  private final AtomicReference<T> resource;\n\n  /**\n   * Use {@link ResourceHolder#fromCloseable}.\n   */\n  CloseableResourceHolder(T resource)\n  {\n    this.resource = new AtomicReference<>(Preconditions.checkNotNull(resource, \"resource\"));\n  }\n\n  @Override\n  public T get()\n  {\n    final T retVal = resource.get();\n    if (retVal == null) {\n      throw new ISE(\"Already closed\");\n    }\n    return retVal;\n  }\n\n  @Override\n  public void close()\n  {\n    final T oldResource = resource.getAndSet(null);\n    CloseableUtils.closeAndWrapExceptions(oldResource);\n  }\n}\n","sourceCodeStart":28,"sourceCodeEnd":58,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/collections/CloseableResourceHolder.java#L28-L58","documentation":"CloseableResourceHolder wraps a lazily-created or shared resource that becomes null once closed. get() refuses to return after close() so callers never use a freed resource, throwing ISE('Already closed').","triggerScenarios":"Calling get() on the holder after close() has been invoked (e.g. using the holder's resource after a service's lifecycle close, or a double-close followed by use).","commonSituations":"Extension/service shutdown ordering where a component still holds the holder and fetches the resource during teardown; caching the holder in a longer-lived object.","solutions":["Obtain the resource before close() and keep your own reference","Fix lifecycle ordering so consumers stop before the holder is closed","Guard with isAvailable()/null-check semantics via the holder API if offered","Do not cache the holder across shutdown boundaries"],"exampleFix":"// before\nholder.close();\nResource r = holder.get(); // ISE\n// after\nResource r = holder.get();\nholder.close();","handlingStrategy":"type-guard","validationCode":"// acquire the resource before close\nT res = holder.get(); // then use res after close only if you kept this reference","typeGuard":"boolean usable = (holder != null); // then call get() before close(); after close(), get() throws by design\n// prefer: T res = holder.get(); /* before close */ ","tryCatchPattern":"try {\n  T res = holder.get();\n} catch (ISE e) {\n  if (\"Already closed\".equals(e.getMessage())) { /* holder was closed; reacquire or skip */ }\n  else throw e;\n}","preventionTips":["Hold your own reference obtained before close()","Order shutdown so consumers stop before the holder closes","Avoid caching the holder past lifecycle boundaries"],"tags":["lifecycle","resource-management"],"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"}