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

  1. Do not use the client/pool after close(); check application lifecycle so shutdown happens only after all work completes.
  2. Reuse a single client instance for the application lifetime instead of closing and recreating it per request.
  3. Guard concurrent access: close the client only when no other threads can issue commands (e.g. after executor termination).
  4. 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

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


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)