apache/pulsar · error · IllegalStateException

No available broker found.

Error message

No available broker found.

What it means

During load-manager migration, RedirectManagerForLoadManagerMigration.redirectIfLoadBalancerOnBrokerIsNotExpected fetches the available-broker lookup data; if the map is empty there is no broker to redirect to or to evaluate, so the returned future fails with IllegalStateException 'No available broker found.'

Solutions

  1. Wait for brokers to start and publish load data, then retry the lookup.
  2. Check broker logs and the metadata store (e.g. /loadbalance/brokers in ZooKeeper et al.) for registered brokers; fix connectivity if empty.
  3. Ensure at least one broker is running and registered with the ExtensibleLoadManager before sending lookups.
  4. Catch the IllegalStateException in the lookup path and return a 503 'Service unavailable' style response to clients.

Example fix

// before
return redirectIfLoadBalancerOnBrokerIsNotExpected(...);
// after
return redirectIfLoadBalancerOnBrokerIsNotExpected(...)
    .exceptionally(ex -> {
        if (ex.getCause() instanceof IllegalStateException) {
            throw new RestException(Response.Status.SERVICE_UNAVAILABLE, "No available brokers");
        }
        throw FutureUtil.wrapToCompletionException(ex);
    });
Defensive patterns

Strategy: try-catch

Validate before calling

boolean hasBrokers = brokerRegistry.snapshot().values().stream()
    .anyMatch(d -> !d.isBrokerShuttingDown());
if (!hasBrokers) { /* defer/redirect with 503 */ }

Try / catch

redirectIfLoadBalancerOnBrokerIsNotExpected(...)
  .exceptionally(ex -> {
      Throwable c = FutureUtil.unwrapCompletionException(ex);
      if (c instanceof IllegalStateException && c.getMessage().contains("No available broker")) {
          throw new RestException(Response.Status.SERVICE_UNAVAILABLE, c.getMessage());
      }
      throw FutureUtil.wrapToCompletionException(c);
  });

Prevention

When it happens

Trigger: A lookup reaches redirectIfLoadBalancerOnBrokerIsNotExpected while getAvailableBrokerLookupDataAsync returns an empty map (no brokers registered in the extensible load manager's broker registry).

Common situations: Cluster just started and no broker has published load data yet, metadata store connectivity issues wiped/emptied the registry, or all brokers were excluded/deregistered (shutdown, crash).

Related errors


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

Appendix: source

Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/manager/RedirectManagerForLoadManagerMigration.java:104

     * Redirect the request to another broker if the load balancer on the current broker is using the load manager
     * of the latest service lookup data available in the metadata store.
     *
     * @param options lookup options
     * @return lookup result
     */
    public CompletableFuture<Optional<LookupResult>> redirectIfLoadBalancerOnBrokerIsNotExpected(
            LookupOptions options) {
        if (!pulsar.getConfiguration().isLoadManagerMigrationEnabled()) {
            // no-op when load manager migration is disabled.
            return CompletableFuture.completedFuture(Optional.empty());
        }
        String currentLMClassName = pulsar.getConfiguration().getLoadManagerClassName();
        boolean debug = ExtensibleLoadManagerImpl.debug(pulsar.getConfiguration(), log);
        return getAvailableBrokerLookupDataAsync().thenApply(lookupDataMap -> {
            if (lookupDataMap.isEmpty()) {
                String errorMsg = "No available broker found.";
                log.warn(errorMsg);
                throw new IllegalStateException(errorMsg);
            }
            AtomicReference<BrokerLookupData> latestServiceLookupData = new AtomicReference<>();
            AtomicLong lastStartTimestamp = new AtomicLong(0L);
            lookupDataMap.forEach((key, value) -> {
                if (lastStartTimestamp.get() <= value.getStartTimestamp()) {
                    lastStartTimestamp.set(value.getStartTimestamp());
                    latestServiceLookupData.set(value);
                }
            });
            if (latestServiceLookupData.get() == null) {
                String errorMsg = "No latest service lookup data found.";
                log.warn(errorMsg);
                throw new IllegalStateException(errorMsg);
            }

            if (Objects.equals(latestServiceLookupData.get().getLoadManagerClassName(), currentLMClassName)) {
                if (debug) {
                    log.info().attr("name", currentLMClassName)

View on GitHub (pinned to 820761864e)