mybatis/mybatis-3 · critical · SQLException

PooledDataSource: Unknown severe error condition. The conne

Error message

PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.

What it means

After PooledDataSource.popConnection() exits its lock-protected retry loop without throwing, conn must be non-null. This exception ('Unknown severe error condition. The connection pool returned a null connection.') is a defensive assertion that fires if the loop terminates with no connection and no other exception — i.e., the pool state reached a combination the implementation considers impossible. In practice it indicates a bug-level anomaly: interrupted waits, race in pool state, or a misconfigured pool where active connections were never available.

Source

Thrown at src/main/java/org/apache/ibatis/datasource/pooled/PooledDataSource.java:546

            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;
  }

  /**
   * Method to check to see if a connection is still usable
   *
   * @param conn
   *          - the connection to check
   *
   * @return True if the connection is still usable
   */
  protected boolean pingConnection(PooledConnection conn) {
    boolean result;

    try {

View on GitHub (pinned to 008069adb1)

Solutions

  1. Check for connection leaks first: every raw Connection from PooledDataSource must be closed (try-with-resources); leaked connections exhaust the pool
  2. Review pool sizing: poolMaximumActiveConnections and poolTimeToWait must cover peak concurrency
  3. Ensure forceCloseAll()/shutdown is not racing live traffic (drain requests before closing the pool)
  4. Avoid interrupting threads blocked in getConnection; if using async frameworks, wrap DB access so cancellation cannot interrupt the pool wait
  5. Upgrade MyBatis if on an old release — pool null-path handling has seen fixes

Example fix

// before: leak exhausts pool -> null/failed getConnection
Connection c = dataSource.getConnection();
if (someCondition) return; // never closed

// after
try (Connection c = dataSource.getConnection()) {
  if (someCondition) return; // closed by try-with-resources
}
Defensive patterns

Strategy: retry

Validate before calling

// Detect pool exhaustion early by monitoring leased vs active counts:
PooledDataSource pds = (PooledDataSource) dataSource;
PoolState state = pds.getPoolState();
if (state.getActiveConnectionCount() >= pds.getPoolMaximumActiveConnections()) {
  // pool exhausted: likely leak — log stack traces, alert, do not block indefinitely
}

Try / catch

try {
  conn = dataSource.getConnection();
} catch (SQLException e) {
  if (e.getMessage().contains("returned a null connection")) {
    // exhaustive condition: check for leaks, then retry after freeing connections
  } else throw e;
}

Prevention

When it happens

Trigger: All pool slots busy and wait timeouts expiring in an unexpected path; thread interruption (InterruptedException) during the connection wait loop; concurrent forceCloseAll racing with popConnection; extremely small pool (poolMaximumActiveConnections exhausted by leaked connections that are never returned).

Common situations: Connection leaks: code obtains connections from PooledDataSource directly and never closes them, exhausting the pool until callers fail; application shutdown racing in-flight getConnection calls; JVM thread interrupts (e.g., timed async frameworks cancelling tasks blocked on getConnection).

Related errors


AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14). Data as JSON: /api/errors/fb077f02d7945f17. Report an issue: GitHub.