brettwooldridge/HikariCP · critical · SQLTransientConnectionException
DataSource returned null unexpectedly
Error message
DataSource returned null unexpectedly
What it means
PoolBase.newConnection() calls dataSource.getConnection() and throws SQLTransientConnectionException if the driver/datasource returns null instead of a Connection. Per JDBC the contract is to never return null, so a null return means a broken or misbehaving DataSource implementation.
Source
Thrown at src/main/java/com/zaxxer/hikari/pool/PoolBase.java:375
*
* @return a Connection
*/
private Connection newConnection(final boolean isEmptyPool) throws Exception
{
final var start = currentTime();
final var id = java.util.UUID.randomUUID();
Connection connection = null;
try {
final var credentials = getCredentials();
final var username = credentials.getUsername();
final var password = credentials.getPassword();
logger.debug("{} - Attempting to create/setup new connection ({})", poolName, id);
connection = (username == null) ? dataSource.getConnection() : dataSource.getConnection(username, password);
if (connection == null) {
throw new SQLTransientConnectionException("DataSource returned null unexpectedly");
}
setupConnection(connection);
lastConnectionFailure.set(null);
connectionFailureTimestamp.set(0);
logger.debug("{} - Established new connection ({})", poolName, id);
return connection;
}
catch (Throwable t) {
logger.debug("{} - Failed to create/setup connection ({}): {}", poolName, id, t.getMessage());
connectionFailureTimestamp.compareAndSet(0, start);
if (isEmptyPool && elapsedMillis(connectionFailureTimestamp.get()) > MINUTES.toMillis(1)) {
logger.warn("{} - Pool is empty, failed to create/setup connection ({})", poolName, id, t);
connectionFailureTimestamp.set(0);
}View on GitHub (pinned to a4d93f4f85)
Solutions
- Fix the custom DataSource: getConnection() must never return null — throw SQLException instead
- If using mocks in tests, stub getConnection() to return a real or proxy Connection
- Verify the underlying driver DataSource is correctly initialized (URL, properties) before wrapping
- Check for wrappers in the chain (Spring AbstractDataSource subclasses) that can return null
Example fix
// before
class RoutingDataSource extends AbstractDataSource {
public Connection getConnection() { return target; } // target may be null
}
// after
class RoutingDataSource extends AbstractDataSource {
public Connection getConnection() throws SQLException {
if (target == null) throw new SQLException("no datasource for tenant " + tenantId);
return target.getConnection();
}
} Defensive patterns
Strategy: validation
Validate before calling
Connection test = wrapperDataSource.getConnection();
if (test == null) throw new SQLException("wrapper returns null; fix it"); Try / catch
try { conn = ds.getConnection(); }
catch (SQLTransientConnectionException e) {
if (e.getMessage().contains("returned null unexpectedly")) {
// defect in custom DataSource: fix its getConnection, not the pool
}
throw e;
} Prevention
- Custom DataSources must throw, never return null, from getConnection
- Stub mocks to return connections, not null
- Test custom wrappers for null returns before plugging into HikariCP
When it happens
Trigger: A custom DataSource wrapper (e.g. tenant-routing or lazy wrappers) whose getConnection() returns null on unknown tenant or missing config; unit-test stubs/mocks returning null; some pooling wrappers that return null on exhaustion instead of throwing.
Common situations: Custom delegating DataSource with a missing branch, Mockito mocks not stubbed for getConnection(), decorating DataSources that forward to a null underlying source.
Related errors
- Wrapped DataSource is not an instance of ${iface}
- Wrapped connection is not an instance of ${iface}
- Wrapped DatabaseMetaData is not an instance of {}
- Wrapped ResultSet is not an instance of {}
- Wrapped statement is not an instance of {}
AI-assisted analysis of brettwooldridge/HikariCP@a4d93f4f85 (2026-08-14).
Data as JSON: /api/errors/be02fde0240139d9.
Report an issue: GitHub.