apache/kafka · error · IllegalArgumentException

topicIdFutures and nameFutures cannot both be specified.

Error message

topicIdFutures and nameFutures cannot both be specified.

What it means

Thrown by the DeleteTopicsResult protected constructor when both topicIdFutures and nameFutures are non-null. The result object is designed to hold exactly one kind of future map — either by topic ID (Uuid) or by topic name — never both. Use the static factories ofTopicIds / ofTopicNames to construct correctly.

Source

Thrown at clients/src/main/java/org/apache/kafka/clients/admin/DeleteTopicsResult.java:38

import org.apache.kafka.common.KafkaFuture;
import org.apache.kafka.common.TopicCollection;
import org.apache.kafka.common.Uuid;
import org.apache.kafka.common.annotation.InterfaceAudience;

import java.util.Collection;
import java.util.Map;

/**
 * The result of the {@link Admin#deleteTopics(Collection)} call.
 */
@InterfaceAudience.Public
public class DeleteTopicsResult {
    private final Map<Uuid, KafkaFuture<Void>> topicIdFutures;
    private final Map<String, KafkaFuture<Void>> nameFutures;

    protected DeleteTopicsResult(Map<Uuid, KafkaFuture<Void>> topicIdFutures, Map<String, KafkaFuture<Void>> nameFutures) {
        if (topicIdFutures != null && nameFutures != null)
            throw new IllegalArgumentException("topicIdFutures and nameFutures cannot both be specified.");
        if (topicIdFutures == null && nameFutures == null)
            throw new IllegalArgumentException("topicIdFutures and nameFutures cannot both be null.");
        this.topicIdFutures = topicIdFutures;
        this.nameFutures = nameFutures;
    }

    static DeleteTopicsResult ofTopicIds(Map<Uuid, KafkaFuture<Void>> topicIdFutures) {
        return new DeleteTopicsResult(topicIdFutures, null);
    }

    static DeleteTopicsResult ofTopicNames(Map<String, KafkaFuture<Void>> nameFutures) {
        return new DeleteTopicsResult(null, nameFutures);
    }

    /**
     * Use when {@link Admin#deleteTopics(TopicCollection, DeleteTopicsOptions)} used a TopicIdCollection
     * @return a map from topic IDs to futures which can be used to check the status of
     * individual deletions if the deleteTopics request used topic IDs. Otherwise return null.

View on GitHub (pinned to 996fb4585a)

Solutions

  1. Use DeleteTopicsResult.ofTopicIds(...) or ofTopicNames(...) instead of the constructor.
  2. If subclassing, pass null for the unused map parameter.
  3. Add a unit test verifying the subclass construction does not supply both maps.

Example fix

// before
new DeleteTopicsResult(idMap, nameMap); // both non-null

// after
DeleteTopicsResult.ofTopicIds(idMap);   // or
DeleteTopicsResult.ofTopicNames(nameMap);
Defensive patterns

Strategy: validation

Validate before calling

// Always use the static factories; never pass both maps
DeleteTopicsResult result = useTopicIds
    ? DeleteTopicsResult.ofTopicIds(idMap)
    : DeleteTopicsResult.ofTopicNames(nameMap);

Prevention

When it happens

Trigger: Directly instantiating DeleteTopicsResult (or a subclass constructor) with both the topicIdFutures map and nameFutures map non-null.

Common situations: A subclass or test code calls the protected constructor with both arguments, violating the either/or invariant. Normal usage goes through the static factory methods, so this is primarily a developer/extension error.

Related errors


AI-assisted analysis of apache/kafka@996fb4585a (2026-08-11). Data as JSON: /api/errors/50c309e21b5a1adb. Report an issue: GitHub.