ThreadPoolBulkheadConfig.from(config) mutates the base config instead of copying it

Author: Develop-KIMCreated Jul 9, 2026Updated Sep 7, 2026
Labelsbug

ThreadPoolBulkheadConfig.from(baseConfig) returns a builder that holds the passed-in config by reference instead of copying its fields:

java
public Builder(ThreadPoolBulkheadConfig bulkheadConfig) {
    this.config = bulkheadConfig;
}

Every subsequent builder call then writes through to the base config, and build() returns that same instance. The other config classes (BulkheadConfig, RateLimiterConfig, TimeLimiterConfig, CircuitBreakerConfig) copy the base config's fields into the builder, so ThreadPoolBulkheadConfig is the only one that behaves this way.

Impact

When several thread-pool-bulkhead instances share a base config (for example the Spring Boot default config, via CommonThreadPoolBulkheadConfigurationProperties which calls from(baseConfig)), the first instance's overrides overwrite the shared base, later instances start from the already-modified base, and context propagators accumulate across instances.

Minimal reproduction (JUnit)

java
@Test
void fromShouldNotMutateBaseConfig() {
    ThreadPoolBulkheadConfig baseConfig = ThreadPoolBulkheadConfig.custom()
        .maxThreadPoolSize(4)
        .coreThreadPoolSize(2)
        .queueCapacity(10)
        .build();

    ThreadPoolBulkheadConfig derivedConfig = ThreadPoolBulkheadConfig.from(baseConfig)
        .maxThreadPoolSize(20)
        .coreThreadPoolSize(8)
        .queueCapacity(50)
        .build();

    assertThat(derivedConfig).isNotSameAs(baseConfig);
    assertThat(baseConfig.getMaxThreadPoolSize()).isEqualTo(4); // actual: 20
}

derivedConfig is the same instance as baseConfig, and baseConfig.getMaxThreadPoolSize() returns 20 instead of 4. The existing createFromBaseConfig test does not catch this because it passes the base config inline and never keeps a reference to it.

Environment

  • resilience4j: master (the same Builder(ThreadPoolBulkheadConfig) is present on released versions)

I have a fix and regression tests ready and can open a PR.

Source: resilience4j/resilience4j