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

  1. Create a new ControlledClusterFailover instance for each PulsarClient.
  2. Close the existing PulsarClient/provider and build a fresh pair instead of re-initializing.
  3. 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

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


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/d90cda57c2ec9f99. Report an issue: GitHub.