{"id":"d84f349e73b771ff","repo":"apache/kafka","slug":"topic-collection-to-subscribe-to-cannot-contain-nu-d84f34","errorCode":null,"errorMessage":"Topic collection to subscribe to cannot contain null or empty topic","messagePattern":"Topic collection to subscribe to cannot contain null or empty topic","errorType":"validation","errorClass":"java.lang.IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/consumer/internals/ClassicKafkaConsumer.java","lineNumber":503,"sourceCode":"     *                 for the subscribed topics\n     * @throws IllegalArgumentException If topics is null or contains null or empty elements\n     * @throws IllegalStateException If {@code subscribe()} is called previously with pattern, or assign is called\n     *                               previously (without a subsequent call to {@link #unsubscribe()}), or if not\n     *                               configured at-least one partition assignment strategy\n     */\n    private void subscribeInternal(Collection<String> topics, Optional<ConsumerRebalanceListener> listener) {\n        acquireAndEnsureOpen();\n        try {\n            throwIfGroupIdNotDefined();\n            if (topics == null)\n                throw new IllegalArgumentException(\"Topic collection to subscribe to cannot be null\");\n            if (topics.isEmpty()) {\n                // treat subscribing to empty topic list as the same as unsubscribing\n                this.unsubscribe();\n            } else {\n                for (String topic : topics) {\n                    if (isBlank(topic))\n                        throw new IllegalArgumentException(\"Topic collection to subscribe to cannot contain null or empty topic\");\n                }\n\n                throwIfNoAssignorsConfigured();\n\n                // Clear the buffered data which are not a part of newly assigned topics\n                final Set<TopicPartition> currentTopicPartitions = new HashSet<>();\n\n                for (TopicPartition tp : subscriptions.assignedPartitions()) {\n                    if (topics.contains(tp.topic()))\n                        currentTopicPartitions.add(tp);\n                }\n\n                fetcher.clearBufferedDataForUnassignedPartitions(currentTopicPartitions);\n\n                log.info(\"Subscribed to topic(s): {}\", String.join(\", \", topics));\n                if (this.subscriptions.subscribe(new HashSet<>(topics), listener))\n                    metadata.requestUpdateForNewTopics();\n            }","sourceCodeStart":485,"sourceCodeEnd":521,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/consumer/internals/ClassicKafkaConsumer.java#L485-L521","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Filter blanks before subscribe: topics = topics.stream().filter(t -> t != null && !t.trim().isEmpty()).collect(toList());","Fix the parser: split and trim, dropping empty tokens (Arrays.stream(raw.split(\",\")).map(String::trim).filter(s -> !s.isEmpty()).collect(toList())).","Fail fast at application startup with a clear message naming the offending topic.","Add a test for the sanitized list."],"exampleFix":"// before\nList<String> topics = Arrays.asList(raw.split(\",\"));\nconsumer.subscribe(topics);\n\n// after\nList<String> topics = Arrays.stream(raw.split(\",\"))\n    .map(String::trim)\n    .filter(s -> !s.isEmpty())\n    .collect(Collectors.toList());\nconsumer.subscribe(topics);","handlingStrategy":"validation","validationCode":"// Identical defence to error 111.\nCollection<String> clean = topics.stream()\n    .filter(t -> t != null && !t.trim().isEmpty())\n    .collect(Collectors.toList());\nif (clean.size() != topics.size()) {\n    log.warn(\"Removed {} blank topic(s) from subscription\", topics.size() - clean.size());\n}\nconsumer.subscribe(clean);","typeGuard":"static boolean allTopicsValid(Collection<String> t) {\n    return t != null && t.stream().allMatch(s -> s != null && !s.trim().isEmpty());\n}","tryCatchPattern":"try {\n    consumer.subscribe(topics);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"null or empty topic\")) {\n        topics = topics.stream().filter(s -> s != null && !s.trim().isEmpty()).toList();\n        consumer.subscribe(topics);\n    } else throw e;\n}","preventionTips":["Sanitize topic lists at the boundary, not at the Kafka call site.","Reject blank topic strings in your config parser with a clear message.","Test that subscribe([\"good\", \"\", null]) is rejected or cleaned."],"tags":["kafka","consumer","classic-consumer","subscribe","validation","configuration"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}