provectus/kafka-ui · warning · ValidationException
Replication factor already equals requested
Error message
Replication factor already equals requested
What it means
TopicsService.getPartitionsReassignments computes a new partition assignment to change a topic's replication factor. If the topic's current replication factor already equals the requested one, no reassignment is possible or needed, and a ValidationException with 'Replication factor already equals requested' is thrown before any assignment is produced.
Solutions
- Check the topic's current replication factor before calling the API and only send a different value
- Delete the redundant call or make it a no-op when target equals current
- Verify the correct topic name was used (a same-factor topic implies no change was intended)
- If an increase/decrease was intended, confirm broker count allows the target factor
Example fix
// before int targetFactor = topic.getReplicationFactor(); // equals current // after int current = describeTopic(topicName).replicationFactor(); int targetFactor = Math.min(current + 1, brokers.size());
Defensive patterns
Strategy: validation
Validate before calling
// Java (caller)
int current = topicsService.getTopicDetails(cluster, topicName)
.getTopicDescription().partitions().get(0).replicas().size();
if (targetFactor == current) {
throw new IllegalArgumentException("Replication factor already " + current + "; nothing to change");
} Try / catch
try {
topicsService.changeReplicationFactor(cluster, topicName, targetFactor);
} catch (ValidationException e) {
if (e.getMessage().contains("already equals requested")) {
log.info("No-op: replication factor already {}", targetFactor); // treat as success
} else { throw e; }
} Prevention
- Fetch current topic config before submitting a replication factor change
- Treat 'already equals requested' as an idempotent success in automation
- Clamp target factor between 1 and broker count
- Avoid re-submitting the same change after retries — check state first
When it happens
Trigger: Calling the changeReplicationFactor API (PUT topic replication factor) with a target replication factor identical to the topic's current replication factor.
Common situations: Re-submitting an already-applied replication factor change; scripted/CI jobs that compute a target factor matching current config; copy-pasted requests using the same factor as the topic.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Something went wrong during adding replicas
- Something went wrong during removing replicas
- seekTo should be set if seekType is
- ' ' is not valid json
- ' ' does not fit schema
AI-assisted analysis of provectus/kafka-ui@83b5a60cc0 (2026-09-08).
Data as JSON: /api/errors/3fad8a776053e979.
Report an issue: GitHub.
Appendix: source
Thrown at kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/TopicsService.java:349
// Iterate brokers and try to remove them from assignment
// while partition replicas count != requested replication factor
for (Integer broker : brokersUsageList) {
// Check is the broker the leader of partition
if (!topic.getPartitions().get(partition).getLeader()
.equals(broker)) {
brokers.remove(broker);
brokersUsage.merge(broker, -1, Integer::sum);
}
if (brokers.size() == replicationFactorChange.getTotalReplicationFactor()) {
break;
}
}
if (brokers.size() != replicationFactorChange.getTotalReplicationFactor()) {
throw new ValidationException("Something went wrong during removing replicas");
}
}
} else {
throw new ValidationException("Replication factor already equals requested");
}
// Return result map
return currentAssignment.entrySet().stream().collect(toMap(
e -> new TopicPartition(topic.getName(), e.getKey()),
e -> Optional.of(new NewPartitionReassignment(e.getValue()))
));
}
private Map<Integer, List<Integer>> getCurrentAssignment(InternalTopic topic) {
return topic.getPartitions().values().stream()
.collect(toMap(
InternalPartition::getPartition,
p -> p.getReplicas().stream()
.map(InternalReplica::getBroker)
.collect(toList())
));
}View on GitHub (pinned to 83b5a60cc0)