{"record":{"id":"44ad3e5e835b7874","repo":"redis/jedis","slug":"pool-is-closed","errorCode":null,"errorMessage":"Pool is closed!","messagePattern":"Pool is closed!","errorType":"exception","errorClass":"JedisConnectionException","httpStatus":null,"severity":"error","filePath":"src/main/java/redis/clients/jedis/mcf/TrackingConnectionPool.java","lineNumber":184,"sourceCode":"        .cache(poolBuilder.cache);\n  }\n\n  public static TrackingConnectionPool from(TrackingConnectionPool existing) {\n    return builder().hostAndPort(existing.hostAndPort).clientConfig(existing.clientConfig)\n        .poolConfig(existing.poolConfig).cache(existing.cache)\n        .maintenanceNotificationsConfig(existing.maintenanceNotificationsConfig).build();\n  }\n\n  @Override\n  public Connection getResource() {\n    try {\n      numWaiters.incrementAndGet();\n      Connection conn = super.getResource();\n      poolTrackedObjects.add(conn);\n      return conn;\n    } catch (Exception e) {\n      if (this.isClosed()) {\n        throw new JedisConnectionException(\"Pool is closed!\", e);\n      }\n      throw e;\n    } finally {\n      numWaiters.decrementAndGet();\n    }\n  }\n\n  @Override\n  public void returnResource(final Connection resource) {\n    super.returnResource(resource);\n    poolTrackedObjects.remove(resource);\n  }\n\n  @Override\n  public void returnBrokenResource(final Connection resource) {\n    super.returnBrokenResource(resource);\n    poolTrackedObjects.remove(resource);\n  }","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/redis/jedis/blob/6dac31d4c224fb3257c216f3985340c6f500cdcb/src/main/java/redis/clients/jedis/mcf/TrackingConnectionPool.java#L166-L202","documentation":"TrackingConnectionPool.getResource() increments a waiter count, delegates to the underlying pool, and tracks the acquired connection. If getResource() throws while the pool has been closed, the original exception is replaced with JedisConnectionException(\"Pool is closed!\", e) so callers get an unambiguous signal that they used a shut-down pool rather than a transient connection failure.","triggerScenarios":"Calling getResource()/issuing commands after pool.close() (client shutdown, forceDisconnect during failover, or application shutdown) — the underlying pool throws IllegalStateException (e.g. 'Pool not open') which is converted here because isClosed() is true.","commonSituations":"Application threads still using a client after shutdown hook closed it; concurrent client.close() during a request surge; reusing a cached client instance that was closed earlier; failover code force-closing pools while in-flight commands still request connections.","solutions":["Do not use the client/pool after close(); check application lifecycle so shutdown happens only after all work completes.","Reuse a single client instance for the application lifetime instead of closing and recreating it per request.","Guard concurrent access: close the client only when no other threads can issue commands (e.g. after executor termination).","Catch JedisConnectionException and recreate/reconnect the client if the pool must be rebuilt after forced shutdown."],"exampleFix":"// before\nclient.close();\nConnection c = client.getResource(); // Pool is closed!\n// after\ntry (JedisConnection c = acquire()) {\n  c.sendCommand(Protocol.Command.PING);\n}\n// close only after all work:\nexecutor.shutdown();\nexecutor.awaitTermination(30, TimeUnit.SECONDS);\nclient.close();","handlingStrategy":"try-catch","validationCode":"// client is single owner; guard with a flag\nboolean closed = false;\nvoid close() { closed = true; client.close(); }\nvoid use() { if (closed) throw new IllegalStateException(\"client closed\"); }","typeGuard":null,"tryCatchPattern":"try {\n  conn = client.getResource();\n} catch (JedisConnectionException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"Pool is closed\")) {\n    client = rebuildClient(); // recreate, not retry\n  } else throw e;\n}","preventionTips":["Close the client only after all worker threads finish (await executor termination).","Keep one long-lived client per application instead of close/recreate cycles.","Do not share the client across lifecycles that close independently."],"tags":["connection-pool","pool-closed","lifecycle","multi-db"],"backgroundTag":"resource-closed","analyzedSha":"6dac31d4c224fb3257c216f3985340c6f500cdcb","analyzedAt":"2026-09-08T04:55:01.204Z","contentChangedAt":"2026-09-08T04:55:01.204Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}