brettwooldridge/HikariCP · error · SQLException

HikariDataSource ${dataSource} has been closed.

Error message

HikariDataSource ${dataSource} has been closed.

What it means

HikariDataSource.getConnection() checks isClosed() and throws SQLException if the pool was already shut down. The pool is sealed/closed either by an explicit close() call or by JVM shutdown hooks, so any subsequent connection request fails immediately.

Source

Thrown at src/main/java/com/zaxxer/hikari/HikariDataSource.java:95

      configuration.copyStateTo(this);

      LOGGER.info("{} - Starting...", configuration.getPoolName());
      pool = fastPathPool = new HikariPool(this);
      LOGGER.info("{} - Start completed.", configuration.getPoolName());

      this.seal();
   }

   // ***********************************************************************
   //                          DataSource methods
   // ***********************************************************************

   /** {@inheritDoc} */
   @Override
   public Connection getConnection() throws SQLException
   {
      if (isClosed()) {
         throw new SQLException("HikariDataSource " + this + " has been closed.");
      }

      if (fastPathPool != null) {
         return fastPathPool.getConnection();
      }

      // See http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java
      HikariPool result = pool;
      if (result == null) {
         synchronized (this) {
            result = pool;
            if (result == null) {
               validate();
               LOGGER.info("{} - Starting...", getPoolName());
               try {
                  pool = result = new HikariPool(this);
                  this.seal();
               }

View on GitHub (pinned to a4d93f4f85)

Solutions

  1. Ensure getConnection() is never called after close(): re-obtain the DataSource from the container/context instead of caching a stale instance
  2. Audit close() calls and shutdown hooks; only close the pool when the application truly stops
  3. In Spring, mark the pool as a container-managed bean and never call close() manually
  4. If a background thread borrows connections, shut it down (or latch it) before closing the pool
  5. As a defensive measure, check isClosed() before borrowing and rebuild the pool if closed (self-healing wrapper)

Example fix

// before
static DataSource DS = createPool(); // cached forever
... later, after DS.close(): DS.getConnection(); // SQLException

// after
DataSource ds = context.getBean(HikariDataSource.class); // always fresh from context
if (ds instanceof HikariDataSource hds && hds.isClosed()) {
   throw new IllegalStateException("pool closed; re-create it");
}
Defensive patterns

Strategy: validation

Validate before calling

if (ds instanceof HikariDataSource hds && hds.isClosed()) {
    // rebuild or fetch a live DataSource instead of calling getConnection()
    throw new IllegalStateException("HikariDataSource closed; obtain a new instance");
}

Try / catch

try { conn = ds.getConnection(); }
catch (SQLException e) {
    if (e.getMessage() != null && e.getMessage().contains("has been closed")) {
        // refresh DataSource reference / re-init pool
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling ds.getConnection() after ds.close(); a Spring context refresh/reload that closes the old bean while application code still holds the old reference; a shutdown hook firing while background threads still borrow connections; storing the DataSource statically and outliving container lifecycle.

Common situations: Hot redeploy in dev, @PreDestroy/close ordering bugs, sharing one HikariDataSource across application restarts, integration tests that close the context between test methods but reuse helpers holding the DataSource.

Related errors


AI-assisted analysis of brettwooldridge/HikariCP@a4d93f4f85 (2026-08-14). Data as JSON: /api/errors/1212a5466bf66841. Report an issue: GitHub.