redis/jedis · error · JedisConnectionException
Pool is closed!
Error message
Pool is closed!
What it means
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.
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.
Example fix
// before
client.close();
Connection c = client.getResource(); // Pool is closed!
// after
try (JedisConnection c = acquire()) {
c.sendCommand(Protocol.Command.PING);
}
// close only after all work:
executor.shutdown();
executor.awaitTermination(30, TimeUnit.SECONDS);
client.close(); Defensive patterns
Strategy: try-catch
Validate before calling
// client is single owner; guard with a flag
boolean closed = false;
void close() { closed = true; client.close(); }
void use() { if (closed) throw new IllegalStateException("client closed"); } Try / catch
try {
conn = client.getResource();
} catch (JedisConnectionException e) {
if (e.getMessage() != null && e.getMessage().contains("Pool is closed")) {
client = rebuildClient(); // recreate, not retry
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Failed to create connection!
- JedisConnectionException
- Failed to evict connections from pool
- is not connected to a Connection.
- controller closed
AI-assisted analysis of redis/jedis@6dac31d4c2 (2026-09-08).
Data as JSON: /api/errors/44ad3e5e835b7874.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/redis/clients/jedis/mcf/TrackingConnectionPool.java:184
.cache(poolBuilder.cache);
}
public static TrackingConnectionPool from(TrackingConnectionPool existing) {
return builder().hostAndPort(existing.hostAndPort).clientConfig(existing.clientConfig)
.poolConfig(existing.poolConfig).cache(existing.cache)
.maintenanceNotificationsConfig(existing.maintenanceNotificationsConfig).build();
}
@Override
public Connection getResource() {
try {
numWaiters.incrementAndGet();
Connection conn = super.getResource();
poolTrackedObjects.add(conn);
return conn;
} catch (Exception e) {
if (this.isClosed()) {
throw new JedisConnectionException("Pool is closed!", e);
}
throw e;
} finally {
numWaiters.decrementAndGet();
}
}
@Override
public void returnResource(final Connection resource) {
super.returnResource(resource);
poolTrackedObjects.remove(resource);
}
@Override
public void returnBrokenResource(final Connection resource) {
super.returnBrokenResource(resource);
poolTrackedObjects.remove(resource);
}View on GitHub (pinned to 6dac31d4c2)