provectus/kafka-ui · error · ValidationException

Topic partition has no leader

Error message

Topic partition %s has no leader

What it means

ReactiveAdminClient.filterPartitionsWithLeaderCheck inspects TopicDescription metadata and throws ValidationException when a partition has no leader broker. Without a leader, offset lookups cannot be served for that partition, so operations like listOffsets would fail; when failOnUnknownLeader is true the client fails fast instead of returning incomplete data. When false, partitions of that topic are silently skipped rather than throwing.

Solutions

  1. Restore broker availability: restart/recover the down broker(s) so a leader is elected for the affected partitions.
  2. Increase the topic's replication factor (>= 2) so leader election can occur when one replica is down.
  3. Retry the offset listing after the controller finishes leader election (the condition is usually transient).
  4. Check `kafka-topics --describe --under-replicated-partitions` / `--unavailable-partitions` and fix the offline partitions.
  5. If using the code path directly, set failOnUnknownLeader=false to skip leaderless topics instead of throwing.

Example fix

// before: fail immediately on leaderless partitions
ListOffsetsResult result = adminClient.listOffsets(offsetsToFetch);
// after: tolerate/skip leaderless partitions
// (in listOffsets call chain) use failOnUnknownLeader=false so
// filterPartitionsWithLeaderCheck skips topics with leaderless partitions
Map<TopicPartition, OffsetSpec> fetchable = filterPartitionsWithLeaderCheck(
    adminClient.describeTopics(names).allTopicNames().get(), fetch, false);
Defensive patterns

Strategy: retry

Validate before calling

const desc = await fetch(`/api/clusters/${cluster}/topics/${topic}`).then(r => r.json());
if (desc.partitions.some(p => p.leader == null)) {
  console.warn(`Topic ${topic} has leaderless partitions; defer offset listing`);
}

Type guard

function hasLeaders(topicDesc) {
  return topicDesc.partitions.every(p => p.leader != null);
}

Try / catch

try {
  offsets = await listTopicOffsets(cluster, topic);
} catch (e) {
  if (e.message.includes('has no leader')) {
    await sleep(backoff); // leader election is usually transient
    offsets = await listTopicOffsets(cluster, topic);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling listTopicOffsets/listOffsets (e.g., GET topic offsets, consumer-group lag calculations) while one or more partitions of the topic have no leader — typically during or right after a broker failure, partition reassignment, or unclean leader election where the metadata still reports leader == null.

Common situations: A broker crashed and the cluster has not finished leader election; replication factor 1 and the only replica (leader) host is down; under-replicated/offline partitions after maintenance or rolling restart; topic reassignment in progress so the partition temporarily has no leader.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of provectus/kafka-ui@83b5a60cc0 (2026-09-08). Data as JSON: /api/errors/f0c7669d246b9bdf. Report an issue: GitHub.

Appendix: source

Thrown at kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/ReactiveAdminClient.java:581

    var targetTopics = partitions.stream().map(TopicPartition::topic).collect(Collectors.toSet());
    return describeTopicsImpl(targetTopics)
        .map(descriptions ->
            filterPartitionsWithLeaderCheck(
                descriptions.values(), partitions::contains, failOnUnknownLeader));
  }

  @VisibleForTesting
  static Set<TopicPartition> filterPartitionsWithLeaderCheck(Collection<TopicDescription> topicDescriptions,
                                                              Predicate<TopicPartition> partitionPredicate,
                                                              boolean failOnUnknownLeader) {
    var goodPartitions = new HashSet<TopicPartition>();
    for (TopicDescription description : topicDescriptions) {
      var goodTopicPartitions = new ArrayList<TopicPartition>();
      for (TopicPartitionInfo partitionInfo : description.partitions()) {
        TopicPartition topicPartition = new TopicPartition(description.name(), partitionInfo.partition());
        if (partitionInfo.leader() == null) {
          if (failOnUnknownLeader) {
            throw new ValidationException(String.format("Topic partition %s has no leader", topicPartition));
          } else {
            // if ANY of topic partitions has no leader - we have to skip all topic partitions
            goodTopicPartitions.clear();
            break;
          }
        }
        if (partitionPredicate.test(topicPartition)) {
          goodTopicPartitions.add(topicPartition);
        }
      }
      goodPartitions.addAll(goodTopicPartitions);
    }
    return goodPartitions;
  }

  // 1. NOTE(!): should only apply for partitions from topics where all partitions have leaders,
  //    otherwise AdminClient will try to fetch topic metadata, fail and retry infinitely (until timeout)
  // 2. NOTE(!): Skips partitions that were not initialized yet

View on GitHub (pinned to 83b5a60cc0)