apache/pulsar · error · IllegalStateException

Pulsar client has been closed, can not build LookupService…

Error message

Pulsar client has been closed, can not build LookupService when calling get lookup with an url

What it means

getLookup(String serviceUrl) lazily builds a LookupService for a URL. If the client has already been closed, building a new lookup service is impossible, so it throws IllegalStateException asking the caller not to build a lookup after close. This surfaces when code holds a client reference past close() and still queries URL-to-lookup mappings.

Solutions

  1. Ensure getLookup is only called while the client is open — check client.isClosed() first or restructure shutdown ordering.
  2. Create a new PulsarClient instance if lookups are needed after the previous one was closed.
  3. Guard shared client references with lifecycle management so no component uses them post-close.

Example fix

// before
client.close();
LookupService ls = client.getLookup(url); // IllegalStateException
// after
if (!client.isClosed()) {
    LookupService ls = client.getLookup(url);
}
Defensive patterns

Strategy: validation

Validate before calling

if (client.isClosed()) {
    throw new IllegalStateException("Refusing getLookup(): client already closed");
}
LookupService ls = client.getLookup(url);

Try / catch

try {
    LookupService ls = client.getLookup(url);
} catch (IllegalStateException e) {
    if (e.getMessage().contains("has been closed")) {
        client = recreateClient(); // rebuild a fresh client for further lookups
        ls = client.getLookup(url);
    } else throw e;
}

Prevention

When it happens

Trigger: Calling client.getLookup(url) (public method) after client.close() has completed, or from another thread while the client is being closed.

Common situations: Shutdown hooks or cleanup code that touches the client after close; long-lived caches holding a closed PulsarClient; race between a close() in one thread and getLookup() in another.

Related errors


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

Appendix: source

Thrown at pulsar-client/src/main/java/org/apache/pulsar/client/impl/PulsarClientImpl.java:1783

    /**
     * Only for test.
     */
    @VisibleForTesting
    public CompletableFuture<ClientCnx> getConnection(final String topic) {
        return getConnection(topic, cnxPool.genRandomKeyToSelectCon()).thenApply(Pair::getLeft);
    }

    public CompletableFuture<ClientCnx> getConnection(final String topic, final String url) {
        TopicName topicName = TopicName.get(topic);
        return getLookup(url).getBroker(topicName)
                .thenCompose(lookupResult -> getConnection(lookupResult.getLogicalAddress(),
                        lookupResult.getPhysicalAddress(), cnxPool.genRandomKeyToSelectCon()));
    }

    public LookupService getLookup(String serviceUrl) {
        return urlLookupMap.computeIfAbsent(serviceUrl, url -> {
            if (isClosed()) {
                throw new IllegalStateException("Pulsar client has been closed, can not build LookupService when"
                        + " calling get lookup with an url");
            }
            try {
                return createLookup(serviceUrl);
            } catch (PulsarClientException e) {
                log.warn().attr("service", url).exceptionMessage(e).log("Failed to update url to lookup service");
                throw new IllegalStateException("Failed to update url " + url);
            }
        });
    }

    public CompletableFuture<ClientCnx> getConnectionToServiceUrl() {
        if (!lookup.isBinaryProtoLookupService()) {
            return FutureUtil.failedFuture(new PulsarClientException.InvalidServiceURL(
                    "Can't get client connection to HTTP service URL", null));
        }
        InetSocketAddress address = lookup.resolveHost();
        return getConnection(address, address, cnxPool.genRandomKeyToSelectCon());

View on GitHub (pinned to 820761864e)