alibaba/druid · error · DataSourceClosedException
dataSource already closed at {date}
Error message
dataSource already closed at {date} What it means
Thrown by fill(int) (the API that pre-populates the pool with physical connections) when the DataSource is already closed. It is a DataSourceClosedException (SQLException subclass) and includes the timestamp at which close() was invoked, so you can tell when the pool was shut down.
Source
Thrown at core/src/main/java/com/alibaba/druid/pool/DruidDataSource.java:3814
this.logDifferentThread = logDifferentThread;
}
public DruidPooledConnection tryGetConnection() throws SQLException {
if (poolingCount == 0) {
return null;
}
return getConnection();
}
@Override
public int fill() throws SQLException {
return this.fill(this.maxActive);
}
@Override
public int fill(int toCount) throws SQLException {
if (closed) {
throw new DataSourceClosedException("dataSource already closed at " + new Date(closeTimeMillis));
}
if (toCount < 0) {
throw new IllegalArgumentException("toCount can't not be less than zero");
}
init();
if (toCount > this.maxActive) {
toCount = this.maxActive;
}
int fillCount = 0;
for (; ; ) {
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
connectErrorCountUpdater.incrementAndGet(this);View on GitHub (pinned to fa8dc99126)
Solutions
- Ensure fill() is called only after init() and before close(); move pre-warming into an init hook (Druid calls fill logic during init when configured).
- If the pool is closed, create a new DruidDataSource rather than calling fill() on the dead one.
- Coordinate shutdown so background fill/warm threads are stopped before the DataSource is closed.
Example fix
// before
@Scheduled(fixedDelay = 60_000)
void warm() { dataSource.fill(maxActive); } // fires after shutdown -> DataSourceClosedException
// after — stop the warmer before the pool closes
@PreDestroy
void shutdown() {
warmScheduler.shutdownNow();
dataSource.close();
} Defensive patterns
Strategy: validation
Validate before calling
if (((DruidDataSource) dataSource).isClosed()) {
throw new IllegalStateException("DataSource closed; cannot fill");
}
return dataSource.fill(toCount); Try / catch
try {
return dataSource.fill(toCount);
} catch (DataSourceClosedException e) {
// pool is gone; either recreate it or skip warming
log.warn("fill skipped, pool closed at {}", e.getMessage());
return 0;
} Prevention
- Call fill() only between init() and close().
- Stop background warmers in @PreDestroy before closing the DataSource.
- Do not reuse a closed DataSource bean — build a new one.
When it happens
Trigger: dataSource.close() has run (closed == true) and then fill() or fill(toCount) is called. The guard at line 3813 throws DataSourceClosedException with new Date(closeTimeMillis).
Common situations: A warm-up/fill routine running on a timer that fires after Spring context shutdown; calling fill() in an @PreDestroy-adjacent phase; reusing a DataSource bean that was closed by a previous test; init/fill ordering mistake in a custom lifecycle.
Related errors
- dataSource already closed at {}
- connection closed
- connection holder is null
- validationQuery didn't return a row
- dataSource inited.
AI-assisted analysis of alibaba/druid@fa8dc99126 (2026-08-14).
Data as JSON: /api/errors/66e6dc0834dd9d33.
Report an issue: GitHub.