alibaba/druid · critical · SQLException
onFatalError, activeCount {}, onFatalErrorMaxActive {}
Error message
onFatalError, activeCount {}, onFatalErrorMaxActive {} What it means
A circuit-breaker: once the pool has recorded a fatal error (onFatalError flag set, e.g. the DB rejected connections or a connection died from a fatal exception), if activeCount has climbed to onFatalErrorMaxActive the pool refuses new borrows and rethrows the last fatal error. This stops a sick database from being flooded with requests after it already signalled failure.
Source
Thrown at core/src/main/java/com/alibaba/druid/pool/DruidDataSource.java:1650
StringBuilder errorMsg = new StringBuilder();
errorMsg.append("onFatalError, activeCount ")
.append(activeCount)
.append(", onFatalErrorMaxActive ")
.append(onFatalErrorMaxActive);
if (lastFatalErrorTimeMillis > 0) {
errorMsg.append(", time '")
.append(StringUtils.formatDateTime19(
lastFatalErrorTimeMillis, TimeZone.getDefault()))
.append("'");
}
if (lastFatalErrorSql != null) {
errorMsg.append(", sql \n")
.append(lastFatalErrorSql);
}
throw new SQLException(
errorMsg.toString(), lastFatalError);
}
connectCount++;
if (createScheduler != null
&& poolingCount == 0
&& activeCount < maxActive
&& createDirectCountUpdater.get(this) == 0
&& creatingCountUpdater.get(this) == 0
&& createScheduler instanceof ScheduledThreadPoolExecutor) {
ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) createScheduler;
if (executor.getQueue().size() > 0) {
if (maxWait > 0 && System.currentTimeMillis() - startTime >= maxWait) {
holder = null;
break;
}
createDirect = true;View on GitHub (pinned to fa8dc99126)
Solutions
- Check the chained lastFatalError — it names the real root cause (e.g. 'Communications link failure'); fix that DB/network issue first.
- Inspect lastFatalErrorTimeMillis and lastFatalErrorSql in the message to locate when and on which query the DB broke.
- Tune onFatalErrorMaxActive upward if you want more headroom during partial outages, or disable the breaker by setting onFatalErrorMaxActive to 0 (then the guard at line 1628 is skipped).
- Add an outer retry/circuit-breaker (e.g. Resilience4j) so callers degrade gracefully while the pool is in fatal state, rather than hammering getConnection().
Example fix
// disable the fatal-error breaker so callers still get normal maxWait timeouts
ddataSource.setOnFatalErrorMaxActive(0);
// or keep it but let callers fall back
try (Connection c = dataSource.getConnection()) { ... }
catch (SQLException e) {
if (dataSource.isOnFatalError()) {
// serve degraded response / trip an upstream circuit breaker
}
} Defensive patterns
Strategy: fallback
Validate before calling
DruidDataSource dds = (DruidDataSource) dataSource;
// isOnFatalError / onFatalError flag plus activeCount vs onFatalErrorMaxActive
if (dds.isOnFatalError() && dds.getOnFatalErrorMaxActive() > 0
&& dds.getActiveCount() >= dds.getOnFatalErrorMaxActive()) {
throw new ServiceUnavailableException("pool in fatal-error breaker state");
}
return dataSource.getConnection(); Try / catch
try {
return dataSource.getConnection();
} catch (SQLException e) {
if (e.getMessage() != null && e.getMessage().startsWith("onFatalError")) {
// delegate to an outer circuit breaker; degrade the request
throw new ServiceUnavailableException("DB fatal breaker open", e);
}
throw e;
} Prevention
- Wrap the DataSource with an outer circuit breaker (Resilience4j) so callers degrade during fatal states.
- Investigate the chained lastFatalError immediately — it is the real DB problem.
- Set onFatalErrorMaxActive to 0 only if you intentionally want to disable the breaker.
When it happens
Trigger: onFatalError is true (a prior fatal SQLException passed the exceptionSorter, e.g. ORA-... / Communications link failure) AND activeCount >= onFatalErrorMaxActive (>0). The caller in getConnection() hits the guard at line 1627-1629 and the assembled error (activeCount, onFatalErrorMaxActive, time, last SQL) is thrown.
Common situations: Database restart or outage under load; network partition between app and DB; DB hitting max_connections so Druid's create attempts fail fatally; onFatalErrorMaxActive set very low (default behaviour) so the breaker trips almost immediately; a bad migration/lock causing fatal errors across many connections.
Related errors
- connection closed
- connection disabled
- create exceptionSorter error
- validationQuery didn't return a row
- maxWaitThreadCount {}, current wait Thread count {}
AI-assisted analysis of alibaba/druid@fa8dc99126 (2026-08-14).
Data as JSON: /api/errors/1935d900e8afbd86.
Report an issue: GitHub.