mybatis/mybatis-3 · critical · SQLException
PooledDataSource: Could not get a good connection to the dat
Error message
PooledDataSource: Could not get a good connection to the database.
What it means
PooledDataSource.popConnection() loops trying to obtain a usable connection. When it repeatedly gets connections that fail pingConnectionDB() validation ('bad connections'), it counts them; once localBadConnectionCount exceeds poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance (default tolerance 3), it throws SQLException 'PooledDataSource: Could not get a good connection to the database.' This means the pool can hand out connections but they are all broken.
Source
Thrown at src/main/java/org/apache/ibatis/datasource/pooled/PooledDataSource.java:532
conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
conn.setCheckoutTimestamp(System.currentTimeMillis());
conn.setLastUsedTimestamp(System.currentTimeMillis());
state.activeConnections.add(conn);
state.requestCount++;
state.accumulatedRequestTime += System.currentTimeMillis() - t;
} else {
if (log.isDebugEnabled()) {
log.debug("A bad connection (" + conn.getRealHashCode()
+ ") was returned from the pool, getting another connection.");
}
state.badConnectionCount++;
localBadConnectionCount++;
conn = null;
if (localBadConnectionCount > poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance) {
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Could not get a good connection to the database.");
}
throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
}
}
}
} finally {
lock.unlock();
}
}
if (conn == null) {
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
}
throw new SQLException(
"PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
}
return conn;View on GitHub (pinned to 008069adb1)
Solutions
- Check that the database is actually reachable and up (the pool found connections but every one failed validation)
- Enable connection validation: set poolPingEnabled=true, poolPingQuery to a cheap valid query (e.g., SELECT 1), and poolPingConnectionsNotUsedFor under the network/firewall idle timeout
- Reduce idle connections: lower poolMaximumIdleConnections so fewer stale sockets are cached, and ensure poolMaximumActiveConnections sizing fits DB limits
- If using a custom poolPingQuery, verify it executes on your DB; if the driver supports it, prefer JDBC isValid by leaving the default ping path
- Raise poolMaximumLocalBadConnectionTolerance only as a stopgap; the root cause is dead connections in the pool
Example fix
<!-- before: no validation, dead connections stay in pool --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://db:3306/app"/> </dataSource> <!-- after --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://db:3306/app"/> <property name="poolPingEnabled" value="true"/> <property name="poolPingQuery" value="select 1"/> <property name="poolPingConnectionsNotUsedFor" value="60000"/> <property name="poolMaximumIdleConnections" value="5"/> </dataSource>
Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight check that the DB is reachable before pool use:
try (Connection c = pooledDataSource.getConnection()) {
if (!c.isValid(2)) throw new SQLException("DB unreachable");
} Try / catch
int attempts = 0;
while (true) {
try (Connection c = pooledDataSource.getConnection()) {
return c.createStatement().executeQuery(q);
} catch (SQLException e) {
if (++attempts >= 3 || !e.getMessage().contains("Could not get a good connection")) throw e;
// pool is full of dead connections; wait for revalidation cycle
}
} Prevention
- Enable poolPingEnabled with a valid poolPingQuery and a poolPingConnectionsNotUsedFor below your firewall idle timeout
- Keep poolMaximumIdleConnections modest so fewer stale sockets accumulate
- Align pool sizing with database and LB connection limits
When it happens
Trigger: poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance consecutive connections fail the PoolState ping query (PoolPingEnabled / poolPingQuery); database restarted or network dropped while pooled connections remained cached; wrong poolPingQuery for the DB dialect; driver incompatibility making isValid()/ping fail; DB max_allowed_packet or firewall idle-killing connections faster than pings detect.
Common situations: Database failover/restart behind a load balancer leaving dead sockets in the pool; aggressive firewall/LB idle timeouts (e.g., 5 min) killing pooled connections; poolPingEnabled=false so dead connections are never validated; custom poolPingQuery that is not valid SQL on the target database; MySQL wait_timeout killing idle connections.
Related errors
- Error accessing PooledConnection. Connection is invalid.
- PooledDataSource: Unknown severe error condition. The conne
- Unknown execution method for: {name}
- Mapper method '{name}' attempted to return null from a metho
- Mapper method '{name}' has an unsupported return type: {retu
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/a7fb8f876a9a5dd4.
Report an issue: GitHub.