{"record":{"id":"65ea8e9767686c1c","repo":"apache/druid","slug":"pollinglookup-id-s-is-closed","errorCode":null,"errorMessage":"pollingLookup id [%s] is closed","messagePattern":"pollingLookup id \\[(.+?)\\] is closed","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions-core/lookups-cached-single/src/main/java/org/apache/druid/server/lookup/PollingLookup.java","lineNumber":145,"sourceCode":"      return (String) cache.get(key);\n    }\n    finally {\n      if (cache != null) {\n        cacheRefKeeper.doneWithIt();\n      }\n    }\n  }\n\n  @Override\n  public List<String> unapply(@Nullable final String value)\n  {\n    if (value == null) {\n      return Collections.emptyList();\n    }\n\n    CacheRefKeeper cacheRefKeeper = refOfCacheKeeper.get();\n    if (cacheRefKeeper == null) {\n      throw new ISE(\"pollingLookup id [%s] is closed\", id);\n    }\n    PollingCache cache = cacheRefKeeper.getAndIncrementRef();\n    try {\n      if (cache == null) {\n        // it must've been closed after swapping while I was getting it.  Try again.\n        return this.unapply(value);\n      }\n      return cache.getKeys(value);\n    }\n    finally {\n      if (cache != null) {\n        cacheRefKeeper.doneWithIt();\n      }\n    }\n  }\n\n  @Override\n  public boolean supportsAsMap()","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-core/lookups-cached-single/src/main/java/org/apache/druid/server/lookup/PollingLookup.java#L127-L163","documentation":"PollingLookup.unapply() (reverse lookup by value) requires the same CacheRefKeeper that apply() uses. If the keeper reference is null the lookup has been closed, and unapply throws an ISE naming the lookup id. It mirrors the apply() guard so reverse mapping cannot run against a torn-down cache.","triggerScenarios":"Calling unapply(value) after close(), or during a concurrent lookup update/close while a query performs reverse lookup.","commonSituations":"Queries using lookup extraction in reverse mode against a lookup that was deleted/refreshed mid-query; holding a lookup reference past its lifecycle in custom extraction code.","solutions":["Re-add or re-register the lookup with the given id, then retry","Ensure the lookup is not closed before calling unapply; check lifecycle ordering in your code","Retry the query after lookup manager synchronization completes"],"exampleFix":"// before\nList<String> keys = pollingLookup.unapply(value);\n// after\nList<String> keys = pollingLookup.isOpen()\n    ? pollingLookup.unapply(value)\n    : Collections.emptyList();","handlingStrategy":"try-catch","validationCode":"if (!pollingLookup.isOpen()) { throw new IllegalStateException(\"lookup \" + id + \" not available for reverse lookup\"); }","typeGuard":"boolean canUnapply(PollingLookup l) { return l != null && l.isOpen(); }","tryCatchPattern":"try { return lookup.unapply(value); } catch (IllegalStateException e) { log.warn(\"lookup closed: {}\", e.getMessage()); return Collections.emptyList(); }","preventionTips":["Track lookup lifecycle events so code stops using a lookup once closed","Synchronize lookup usage with lookup manager refresh notifications","Verify the lookup id still exists in cluster lookup config before reverse lookups"],"tags":["lookup","closed-resource","reverse-lookup","state"],"backgroundTag":"internal-invariant-violation","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}