{"record":{"id":"be02fde0240139d9","repo":"brettwooldridge/HikariCP","slug":"datasource-returned-null-unexpectedly","errorCode":null,"errorMessage":"DataSource returned null unexpectedly","messagePattern":"DataSource returned null unexpectedly","errorType":"exception","errorClass":"SQLTransientConnectionException","httpStatus":null,"severity":"critical","filePath":"src/main/java/com/zaxxer/hikari/pool/PoolBase.java","lineNumber":375,"sourceCode":"    *\n    * @return a Connection\n    */\n   private Connection newConnection(final boolean isEmptyPool) throws Exception\n   {\n      final var start = currentTime();\n      final var id = java.util.UUID.randomUUID();\n\n      Connection connection = null;\n      try {\n         final var credentials = getCredentials();\n         final var username = credentials.getUsername();\n         final var password = credentials.getPassword();\n\n         logger.debug(\"{} - Attempting to create/setup new connection ({})\", poolName, id);\n\n         connection = (username == null) ? dataSource.getConnection() : dataSource.getConnection(username, password);\n         if (connection == null) {\n            throw new SQLTransientConnectionException(\"DataSource returned null unexpectedly\");\n         }\n\n         setupConnection(connection);\n\n         lastConnectionFailure.set(null);\n         connectionFailureTimestamp.set(0);\n\n         logger.debug(\"{} - Established new connection ({})\", poolName, id);\n         return connection;\n      }\n      catch (Throwable t) {\n         logger.debug(\"{} - Failed to create/setup connection ({}): {}\", poolName, id, t.getMessage());\n\n         connectionFailureTimestamp.compareAndSet(0, start);\n         if (isEmptyPool && elapsedMillis(connectionFailureTimestamp.get()) > MINUTES.toMillis(1)) {\n            logger.warn(\"{} - Pool is empty, failed to create/setup connection ({})\", poolName, id, t);\n            connectionFailureTimestamp.set(0);\n         }","sourceCodeStart":357,"sourceCodeEnd":393,"githubUrl":"https://github.com/brettwooldridge/HikariCP/blob/a4d93f4f85517f90e632b795486d7102e933d7ff/src/main/java/com/zaxxer/hikari/pool/PoolBase.java#L357-L393","documentation":"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.","triggerScenarios":"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.","commonSituations":"Custom delegating DataSource with a missing branch, Mockito mocks not stubbed for getConnection(), decorating DataSources that forward to a null underlying source.","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"],"exampleFix":"// before\nclass RoutingDataSource extends AbstractDataSource {\n   public Connection getConnection() { return target; } // target may be null\n}\n\n// after\nclass RoutingDataSource extends AbstractDataSource {\n   public Connection getConnection() throws SQLException {\n      if (target == null) throw new SQLException(\"no datasource for tenant \" + tenantId);\n      return target.getConnection();\n   }\n}","handlingStrategy":"validation","validationCode":"Connection test = wrapperDataSource.getConnection();\nif (test == null) throw new SQLException(\"wrapper returns null; fix it\");","typeGuard":null,"tryCatchPattern":"try { conn = ds.getConnection(); }\ncatch (SQLTransientConnectionException e) {\n    if (e.getMessage().contains(\"returned null unexpectedly\")) {\n        // defect in custom DataSource: fix its getConnection, not the pool\n    }\n    throw e;\n}","preventionTips":["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"],"tags":["hikaricp","jdbc","custom-datasource","null-contract"],"backgroundTag":null,"analyzedSha":"a4d93f4f85517f90e632b795486d7102e933d7ff","analyzedAt":"2026-08-14T12:11:37.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}