brettwooldridge/HikariCP · error · IllegalStateException

The configuration of the pool is sealed once started. Use Hi

Error message

The configuration of the pool is sealed once started. Use HikariConfigMXBean for runtime changes.

What it means

Once a HikariDataSource/HikariPool has started, HikariConfig is sealed and mutating setters that call checkIfSealed() (setJdbcUrl, setDriverClassName, setHealthCheckRegistry, setCredentialsProviderClassName, setExceptionOverrideClassName, etc.) throw IllegalStateException telling you to use HikariConfigMXBean for runtime changes. This prevents mutating a live pool's identity/behavior fields behind its back; only specific tunables (pool size, timeouts via the MXBean) are runtime-changeable.

Source

Thrown at src/main/java/com/zaxxer/hikari/HikariConfig.java:1156

         minIdle = maxPoolSize;
      }

      if (idleTimeout + SECONDS.toMillis(1) > maxLifetime && maxLifetime > 0 && minIdle < maxPoolSize) {
         LOGGER.warn("{} - idleTimeout is close to or more than maxLifetime, disabling it.", poolName);
         idleTimeout = 0;
      }
      else if (idleTimeout != 0 && idleTimeout < SECONDS.toMillis(10) && minIdle < maxPoolSize) {
         LOGGER.warn("{} - idleTimeout is less than 10000ms, setting to default {}ms.", poolName, IDLE_TIMEOUT);
         idleTimeout = IDLE_TIMEOUT;
      }
      else  if (idleTimeout != IDLE_TIMEOUT && idleTimeout != 0 && minIdle == maxPoolSize) {
         LOGGER.warn("{} - idleTimeout has been set but has no effect because the pool is operating as a fixed size pool.", poolName);
      }
   }

   private void checkIfSealed()
   {
      if (sealed) throw new IllegalStateException("The configuration of the pool is sealed once started. Use HikariConfigMXBean for runtime changes.");
   }

   private void logConfiguration()
   {
      LOGGER.debug("{} - configuration:", poolName);
      final var propertyNames = new TreeSet<>(PropertyElf.getPropertyNames(HikariConfig.class));
      for (var prop : propertyNames) {
         try {
            var value = PropertyElf.getProperty(prop, this);
            if ("dataSourceProperties".equals(prop)) {
               var dsProps = PropertyElf.copyProperties(dataSourceProperties);
               dsProps.setProperty("password", "<masked>");
               value = dsProps;
            }

            if ("initializationFailTimeout".equals(prop) && initializationFailTimeout == Long.MAX_VALUE) {
               value = "infinite";
            }

View on GitHub (pinned to a4d93f4f85)

Solutions

  1. Create a new HikariConfig/HikariDataSource for the new settings instead of mutating a started one — copyStateTo(HikariConfig) exists for cloning
  2. For legitimately runtime-tunable values (maximumPoolSize, idleTimeout, maxLifetime, connectionTimeout, minimumIdle), cast to HikariConfigMXBean and call its setters
  3. Restructure tests to build a fresh DataSource per test or per context rather than reconfiguring a shared one
  4. Do all configuration before new HikariDataSource(config) / before the container/before Spring context finishes binding

Example fix

// before
HikariDataSource ds = ...; // already started
ds.setJdbcUrl(newUrl); // IllegalStateException: sealed

// after: build a new pool
HikariConfig cfg = new HikariConfig();
existingConfig.copyStateTo(cfg);
cfg.setJdbcUrl(newUrl);
HikariDataSource newDs = new HikariDataSource(cfg);
// runtime-legal resize of the original pool:
((HikariConfigMXBean) ds).setMaximumPoolSize(20);
Defensive patterns

Strategy: validation

Validate before calling

if (dataSource.isRunning()) { // pool already started
   throw new IllegalStateException("HikariConfig is sealed after start; create a new HikariConfig/HikariDataSource or use HikariConfigMXBean");
}

Type guard

static boolean isMutable(HikariDataSource ds) { return !ds.isRunning(); }
// or for runtime tunables only:
static boolean isRuntimeTunable(String property) {
   return java.util.Set.of("maximumPoolSize","minimumIdle","idleTimeout","maxLifetime","connectionTimeout").contains(property);
}

Try / catch

catch (IllegalStateException e) {
   if (e.getMessage().contains("sealed")) {
      // rebuild pool from a copied config instead of mutating
      HikariConfig fresh = new HikariConfig();
      oldConfig.copyStateTo(fresh);
      fresh.setJdbcUrl(newUrl);
      var newDs = new HikariDataSource(fresh);
      // swap references, then oldDs.close()
   } else throw e;
}

Prevention

When it happens

Trigger: Calling setters like setDriverClassName/setJdbcUrl/setDataSourceProperties on a HikariDataSource after it was started — commonly by reusing a started config object for a second pool, Spring's property binding applying to an already-initialized DataSource, or test code mutating a shared singleton pool's config between tests.

Common situations: @Autowired HikariDataSource and then calling its setters at runtime; test suites that reuse one static DataSource and reconfigure per test; frameworks that bind properties to an existing bean; mistakenly treating HikariConfig as a mutable runtime handle.

Related errors


AI-assisted analysis of brettwooldridge/HikariCP@a4d93f4f85 (2026-08-14). Data as JSON: /api/errors/3fa456745c1bc126. Report an issue: GitHub.