apache/kafka · error · java.lang.IllegalArgumentException

Topic collection to subscribe to cannot contain null or empt

Error message

Topic collection to subscribe to cannot contain null or empty topic

What it means

Thrown by ClassicKafkaConsumer.subscribeInternal when any element of the topics collection is null, empty, or whitespace (isBlank check). The classic consumer validates each topic name before contacting the coordinator so that a malformed name does not surface as an obscure broker error. It is the per-element equivalent of the null-collection guard.

Source

Thrown at clients/src/main/java/org/apache/kafka/clients/consumer/internals/ClassicKafkaConsumer.java:503

     *                 for the subscribed topics
     * @throws IllegalArgumentException If topics is null or contains null or empty elements
     * @throws IllegalStateException If {@code subscribe()} is called previously with pattern, or assign is called
     *                               previously (without a subsequent call to {@link #unsubscribe()}), or if not
     *                               configured at-least one partition assignment strategy
     */
    private void subscribeInternal(Collection<String> topics, Optional<ConsumerRebalanceListener> listener) {
        acquireAndEnsureOpen();
        try {
            throwIfGroupIdNotDefined();
            if (topics == null)
                throw new IllegalArgumentException("Topic collection to subscribe to cannot be null");
            if (topics.isEmpty()) {
                // treat subscribing to empty topic list as the same as unsubscribing
                this.unsubscribe();
            } else {
                for (String topic : topics) {
                    if (isBlank(topic))
                        throw new IllegalArgumentException("Topic collection to subscribe to cannot contain null or empty topic");
                }

                throwIfNoAssignorsConfigured();

                // Clear the buffered data which are not a part of newly assigned topics
                final Set<TopicPartition> currentTopicPartitions = new HashSet<>();

                for (TopicPartition tp : subscriptions.assignedPartitions()) {
                    if (topics.contains(tp.topic()))
                        currentTopicPartitions.add(tp);
                }

                fetcher.clearBufferedDataForUnassignedPartitions(currentTopicPartitions);

                log.info("Subscribed to topic(s): {}", String.join(", ", topics));
                if (this.subscriptions.subscribe(new HashSet<>(topics), listener))
                    metadata.requestUpdateForNewTopics();
            }

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Filter blanks before subscribe: topics = topics.stream().filter(t -> t != null && !t.trim().isEmpty()).collect(toList());
  2. Fix the parser: split and trim, dropping empty tokens (Arrays.stream(raw.split(",")).map(String::trim).filter(s -> !s.isEmpty()).collect(toList())).
  3. Fail fast at application startup with a clear message naming the offending topic.
  4. Add a test for the sanitized list.

Example fix

// before
List<String> topics = Arrays.asList(raw.split(","));
consumer.subscribe(topics);

// after
List<String> topics = Arrays.stream(raw.split(","))
    .map(String::trim)
    .filter(s -> !s.isEmpty())
    .collect(Collectors.toList());
consumer.subscribe(topics);
Defensive patterns

Strategy: validation

Validate before calling

// Identical defence to error 111.
Collection<String> clean = topics.stream()
    .filter(t -> t != null && !t.trim().isEmpty())
    .collect(Collectors.toList());
if (clean.size() != topics.size()) {
    log.warn("Removed {} blank topic(s) from subscription", topics.size() - clean.size());
}
consumer.subscribe(clean);

Type guard

static boolean allTopicsValid(Collection<String> t) {
    return t != null && t.stream().allMatch(s -> s != null && !s.trim().isEmpty());
}

Try / catch

try {
    consumer.subscribe(topics);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("null or empty topic")) {
        topics = topics.stream().filter(s -> s != null && !s.trim().isEmpty()).toList();
        consumer.subscribe(topics);
    } else throw e;
}

Prevention

When it happens

Trigger: consumer.subscribe(Arrays.asList("orders", "")); consumer.subscribe(Arrays.asList("orders", null)); consumer.subscribe(Arrays.asList("orders", " ")); passing a list parsed from a comma-separated config string with empty trailing tokens.

Common situations: Comma-separated topic config with trailing comma or double comma ("orders,,payments"); JSON/YAML lists containing nulls; topic lists built from file lines with blank lines included; environment overrides that produce empty strings.

Related errors


AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03). Data as JSON: /data/errors/d84f349e73b771ff.json. Report an issue: GitHub.