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
- Ensure getConnection() is never called after close(): re-obtain the DataSource from the container/context instead of caching a stale instance
- Audit close() calls and shutdown hooks; only close the pool when the application truly stops
- In Spring, mark the pool as a container-managed bean and never call close() manually
- If a background thread borrows connections, shut it down (or latch it) before closing the pool
- 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
- Do not cache HikariDataSource in static fields across container restarts
- Close pools only in application shutdown hooks / @PreDestroy after workers stop
- Check isClosed() before borrowing from long-lived references
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
- Connection is closed
- The configuration of the pool is sealed once started. Use Hi
- MetricRegistry can only be set one time
- MetricsTrackerFactory can only be set one time
- HealthCheckRegistry can only be set one time
AI-assisted analysis of brettwooldridge/HikariCP@a4d93f4f85 (2026-08-14).
Data as JSON: /api/errors/1212a5466bf66841.
Report an issue: GitHub.