apache/pulsar · error · IllegalStateException
ServiceUrlProvider has already been initialized
Error message
ServiceUrlProvider has already been initialized
What it means
ControlledClusterFailover is a ServiceUrlProvider and initialize(PulsarClient) is meant to be called exactly once by the client to wire it up. Calling initialize again while it already holds a pulsarClient reference throws IllegalStateException to prevent leaking HTTP clients and re-binding to a second client.
Source
Thrown at pulsar-client/src/main/java/org/apache/pulsar/client/impl/ControlledClusterFailover.java:125
confBuilder.setKeepAliveStrategy(new DefaultKeepAliveStrategy() {
@Override
public boolean keepAlive(InetSocketAddress remoteAddress, Request ahcRequest,
HttpRequest request, HttpResponse response) {
// Close connection upon a server error or per HTTP spec
return (response.status().code() / 100 != 5)
&& super.keepAlive(remoteAddress, ahcRequest, request, response);
}
});
confBuilder.setNettyTimer(pulsarClient.timer());
confBuilder.setEventLoopGroup(pulsarClient.eventLoopGroup());
AsyncHttpClientConfig config = confBuilder.build();
return new DefaultAsyncHttpClient(config);
}
@Override
public synchronized void initialize(PulsarClient client) {
if (this.pulsarClient != null) {
throw new IllegalStateException("ServiceUrlProvider has already been initialized");
}
this.pulsarClient = (PulsarClientImpl) client;
this.httpClient = buildHttpClient();
this.requestBuilder = httpClient.prepareGet(urlProvider)
// share Pulsar client DNS resolver and cache
.setNameResolver(pulsarClient.getNameResolver())
.addHeader("Accept", "application/json");
headers.forEach(requestBuilder::addHeader);
// Initialize currentControlledConfiguration from client's current configuration
// to avoid unnecessary reconnection on first scheduled check when the configuration hasn't changed
ClientConfigurationData conf = pulsarClient.getConfiguration();
this.currentControlledConfiguration = new ControlledConfiguration();
this.currentControlledConfiguration.setServiceUrl(currentPulsarServiceUrl);
this.currentControlledConfiguration.setTlsTrustCertsFilePath(conf.getTlsTrustCertsFilePath());
this.currentControlledConfiguration.setAuthPluginClassName(conf.getAuthPluginClassName());
this.currentControlledConfiguration.setAuthParamsString(conf.getAuthParams());
View on GitHub (pinned to 820761864e)
Solutions
- Create a new ControlledClusterFailover instance for each PulsarClient.
- Close the existing PulsarClient/provider and build a fresh pair instead of re-initializing.
- In tests, construct the provider inside the test setup for each case rather than sharing a field.
Example fix
// before provider.initialize(client1); provider.initialize(client2); // throws // after ControlledClusterFailover p2 = ControlledClusterFailover.builder().urlProvider(...).defaultServiceUrl(...).build(); p2.initialize(client2);
Defensive patterns
Strategy: validation
Validate before calling
if (provider instanceof ControlledClusterFailover ccf) {
// build a fresh instance per client instead of re-initializing
} Try / catch
try {
provider.initialize(client);
} catch (IllegalStateException e) {
provider = ControlledClusterFailover.builder()...build(); // recreate
provider.initialize(client);
} Prevention
- Create a new ServiceUrlProvider instance for every PulsarClient.
- Never call initialize() twice on the same provider; close and rebuild instead.
- In tests, instantiate the provider in setup per test case.
When it happens
Trigger: Calling provider.initialize(client) a second time on the same ControlledClusterFailover instance — e.g. building two PulsarClient instances that share one provider, or re-initializing in tests (as seen in testBuildControlledClusterFailoverInstance / testControlledClusterFailoverSwitch).
Common situations: Reusing a single ServiceUrlProvider across multiple PulsarClient instances; unit tests constructing the client twice; hot-reload code that re-runs initialize without recreating the provider.
Related errors
- Broker doesn't allow forced deletion of namespaces
- Local cluster is not part of replicate cluster list
- Entry filter '${filterName}' not found
- No entry filter is found for name `${filterName}`. Available
- Invalid auto split/merge configuration: ${message}
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/d90cda57c2ec9f99.
Report an issue: GitHub.