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

  1. 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).
  2. If the pool is closed, create a new DruidDataSource rather than calling fill() on the dead one.
  3. 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

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


AI-assisted analysis of alibaba/druid@fa8dc99126 (2026-08-14). Data as JSON: /api/errors/66e6dc0834dd9d33. Report an issue: GitHub.