brettwooldridge/HikariCP · error · SQLException
Connection is closed
Error message
Connection is closed
What it means
When a HikariCP proxy connection has been closed (or its entry evicted), the delegate is replaced with a synthetic ClosedConnection dynamic proxy. Any method call other than close()/isClosed()/isValid()/abort()/toString() on it throws SQLException 'Connection is closed'. This makes use-after-close fail fast instead of touching a recycled real connection.
Source
Thrown at src/main/java/com/zaxxer/hikari/pool/ProxyConnection.java:559
private static Connection getClosedConnection()
{
InvocationHandler handler = (proxy, method, args) -> {
final String methodName = method.getName();
switch (methodName) {
case "isClosed":
return Boolean.TRUE;
case "isValid":
return Boolean.FALSE;
case "abort":
return Void.TYPE;
case "close":
return Void.TYPE;
case "toString":
return ClosedConnection.class.getCanonicalName();
}
throw new SQLException("Connection is closed");
};
return (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), new Class[] { Connection.class }, handler);
}
}
}
View on GitHub (pinned to a4d93f4f85)
Solutions
- Use try-with-resources so connections cannot be used after close
- Do not cache connections across requests/threads; borrow per unit of work
- Ensure async work completes before the owning request returns the connection
- Check isClosed() defensively in code paths that may outlive the borrow scope
Example fix
// before
Connection c = ds.getConnection();
use(c); c.close();
useLater(c); // SQLException: Connection is closed
// after
try (Connection c = ds.getConnection()) {
use(c);
useLater(c); // inside scope
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!conn.isClosed()) { conn.prepareStatement(...); } Try / catch
try { stmt = conn.prepareStatement(sql); }
catch (SQLException e) {
if ("Connection is closed".equals(e.getMessage())) { /* re-borrow a connection and retry the unit of work */ }
else throw e;
} Prevention
- Always use try-with-resources for connections/statements
- Never share a connection across threads or requests
- Complete async work before the owner returns the connection
When it happens
Trigger: Using a Connection after close() (explicit or try-with-resources exit); holding a connection past connectionTimeout/maxLifetime and continuing after HikariCP evicts it; async code capturing the connection and running after the request returned it to the pool.
Common situations: Forgotten close ordering, connection leaked then recycled to another thread, background jobs using a request-scoped connection, frameworks returning connections early (e.g. after transaction commit in some setups).
Related errors
- HikariDataSource ${dataSource} has been 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/4a788059cef3e85f.
Report an issue: GitHub.