testcontainers/testcontainers-java · error · IllegalStateException

Changing startup timeout is not supported with mode

Error message

Changing startup timeout is not supported with mode %s

What it means

WaitAllStrategy in Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY lets each inner wait strategy own its timeout; the outer strategy therefore forbids overriding a global one. Calling withStartupTimeout on such an instance throws this IllegalStateException at configuration time (before any container starts).

Solutions

  1. Remove the withStartupTimeout call and set timeouts on each child strategy instead
  2. Use WaitAllStrategy.Mode.WITH_OUTER_TIMEOUT (default) if you want a single global timeout
  3. Wrap construction in your own helper so the timeout call is only applied for compatible modes

Example fix

// before
new WaitAllStrategy<>(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY)
    .withStrategy(new HttpWaitStrategy().withStartupTimeout(Duration.ofSeconds(30)))
    .withStartupTimeout(Duration.ofSeconds(90));
// after
new WaitAllStrategy<>(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY)
    .withStrategy(new HttpWaitStrategy().withStartupTimeout(Duration.ofSeconds(30)));
Defensive patterns

Strategy: validation

Validate before calling

WaitAllStrategy<WaitStrategy> s = new WaitAllStrategy<>(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY);
// Only call withStartupTimeout when the mode permits it
if (mode != WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY) {
    s.withStartupTimeout(Duration.ofSeconds(90));
}

Try / catch

try {
    return allStrategy.withStartupTimeout(timeout);
} catch (IllegalStateException e) {
    if (e.getMessage().contains("Changing startup timeout is not supported")) {
        return allStrategy; // keep per-child timeouts
    }
    throw e;
}

Prevention

When it happens

Trigger: new WaitAllStrategy<>(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY).withStartupTimeout(Duration.ofSeconds(90)) — or builder chaining that applies withStartupTimeout after selecting that mode.

Common situations: Copy-pasting a shared wait-strategy builder that always sets withStartupTimeout; switching the mode constant without removing the timeout call; combining per-strategy timeouts configured on children with an accidental global one.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of testcontainers/testcontainers-java@8e549514e3 (2026-09-12). Data as JSON: /api/errors/ff1a61e0a410141e. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/testcontainers/containers/wait/strategy/WaitAllStrategy.java:82

    private void waitUntilNestedStrategiesAreReady(WaitStrategyTarget waitStrategyTarget) {
        for (WaitStrategy strategy : strategies) {
            strategy.waitUntilReady(waitStrategyTarget);
        }
    }

    public WaitAllStrategy withStrategy(WaitStrategy strategy) {
        if (mode == Mode.WITH_OUTER_TIMEOUT) {
            applyStartupTimeout(strategy);
        }

        this.strategies.add(strategy);
        return this;
    }

    @Override
    public WaitAllStrategy withStartupTimeout(Duration startupTimeout) {
        if (mode == Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY) {
            throw new IllegalStateException(
                String.format(
                    "Changing startup timeout is not supported with mode %s",
                    Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY
                )
            );
        }

        this.timeout = startupTimeout;
        strategies.forEach(this::applyStartupTimeout);
        return this;
    }

    private void applyStartupTimeout(WaitStrategy childStrategy) {
        childStrategy.withStartupTimeout(this.timeout);
    }
}

View on GitHub (pinned to 8e549514e3)