apache/pulsar · error · IllegalStateException

No latest service lookup data found.

Error message

No latest service lookup data found.

What it means

After collecting available brokers, redirectIfLoadBalancerOnBrokerIsNotExpected picks the broker with the latest start timestamp as the 'latest service'. If no entry won (map non-empty but no data set, defensive case), the future fails with IllegalStateException 'No latest service lookup data found.'

Source

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

        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)
                            .log("No need to redirect, current load manager class name");
                }
                return Optional.empty();
            }
            var serviceLookupDataObj = latestServiceLookupData.get();
            var candidateBrokers = new ArrayList<BrokerLookupData>();
            lookupDataMap.forEach((key, value) -> {
                if (Objects.equals(value.getLoadManagerClassName(), serviceLookupDataObj.getLoadManagerClassName())) {
                    candidateBrokers.add(value);
                }
            });
            var selectedBroker = candidateBrokers.get((int) (Math.random() * candidateBrokers.size()));

View on GitHub (pinned to 820761864e)

Solutions

  1. Verify broker lookup data has sane non-negative start timestamps; re-register/restart brokers with bad data.
  2. Re-check metadata-store connectivity — a concurrent deregistration of all brokers can empty the map mid-flight.
  3. Retry the lookup; this is usually transient.
  4. In tests, ensure stubbed BrokerLookupData has startTimestamp >= 0.

Example fix

// before (test)
new BrokerLookupData(...).setStartTimestamp(-1);
// after
new BrokerLookupData(...).setStartTimestamp(System.currentTimeMillis());
Defensive patterns

Strategy: retry

Validate before calling

if (lookupDataMap.isEmpty()) throw new RestException(Response.Status.SERVICE_UNAVAILABLE, "no brokers");
boolean anyValid = lookupDataMap.values().stream()
    .anyMatch(d -> d.getStartTimestamp() >= 0);
if (!anyValid) throw new RestException(Response.Status.SERVICE_UNAVAILABLE, "invalid lookup data");

Try / catch

CompletableFuture.supplyAsync(() -> redirectIfLoadBalancerOnBrokerIsNotExpected(...))
  .orTimeout(3, TimeUnit.SECONDS)
  .exceptionally(ex -> { /* retry lookup once, then 503 */ return null; });

Prevention

When it happens

Trigger: getAvailableBrokerLookupDataAsync returned a non-empty map but the forEach never set latestServiceLookupData — only possible with pathological lookup data (e.g. negative/NaN-like start timestamps never satisfying lastStartTimestamp <= value.getStartTimestamp() for a first negative value... in practice the initial 0L is overwritten by any timestamp >= 0, so this fires only when the map was emptied concurrently or timestamps are negative).

Common situations: Race where the broker registry is emptied between the emptiness check and the iteration, or corrupted start timestamps in broker lookup data; mostly seen in tests stubbing lookup data with negative start timestamps.

Related errors


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