{"id":"43b8a7d6571f31e3","repo":"apache/kafka","slug":"topic-collection-to-subscribe-to-cannot-be-null-43b8a7","errorCode":null,"errorMessage":"Topic collection to subscribe to cannot be null","messagePattern":"Topic collection to subscribe to cannot be null","errorType":"validation","errorClass":"java.lang.IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/consumer/internals/ClassicKafkaConsumer.java","lineNumber":496,"sourceCode":"     * with manual partition assignment through {@link #assign(Collection)}.\n     *\n     * If the given list of topics is empty, it is treated the same as {@link #unsubscribe()}.\n     *\n     * <p>\n     * @param topics The list of topics to subscribe to\n     * @param listener {@link Optional} listener instance to get notifications on partition assignment/revocation\n     *                 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                }","sourceCodeStart":478,"sourceCodeEnd":514,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/consumer/internals/ClassicKafkaConsumer.java#L478-L514","documentation":"Thrown by ClassicKafkaConsumer.subscribeInternal as an IllegalArgumentException when the topics collection is null. It mirrors the contract of the async consumer: subscription is defined only for a non-null collection, and a null is treated as a programming error rather than an unsubscribe. The guard fires before any coordinator interaction so the caller sees the mistake immediately.","triggerScenarios":"Calling consumer.subscribe((Collection<String>) null) on a ClassicKafkaConsumer; frameworks (Spring Kafka, Quarkus) that forward a nullable topic list into the classic consumer; reflection-based wiring where the topic list field is still null at subscribe time.","commonSituations":"Topic list loaded lazily and not yet populated when subscribe is called; DI container ordering; config-driven topic lists where the config key is missing and the loader returns null instead of an empty list; migration between assign() (which has different null semantics) and subscribe().","solutions":["Pass a non-null collection: consumer.subscribe(Collections.singletonList(\"orders\"));","Ensure config loaders return an empty list instead of null when no topics are configured, then decide whether to subscribe or unsubscribe based on emptiness.","Guard at the call site: if (topics != null) consumer.subscribe(topics);","Add an integration test exercising the subscribe path with a populated list."],"exampleFix":"// before\nconsumer.subscribe((Collection<String>) null);\n\n// after\nList<String> topics = config.topics() != null ? config.topics() : List.of();\nif (!topics.isEmpty()) consumer.subscribe(topics); else consumer.unsubscribe();","handlingStrategy":"validation","validationCode":"// Identical defence to error 110.\nif (topics == null) {\n    throw new IllegalArgumentException(\"topics must not be null\");\n}\nconsumer.subscribe(topics);","typeGuard":"// Wrap as Optional to force explicit handling.\nOptional.ofNullable(topics)\n        .orElseThrow(() -> new IllegalArgumentException(\"topics is null\"))\n        .forEach(consumer::subscribe);","tryCatchPattern":"try {\n    consumer.subscribe(topics);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"cannot be null\")) {\n        log.warn(\"Null topics on classic consumer; ignoring subscribe\");\n    } else throw e;\n}","preventionTips":["Default topic collections to empty List rather than null.","Centralize subscribe calls behind one wrapper that enforces non-null.","Static-analysis rule: @NonNull on method parameters feeding subscribe()."],"tags":["kafka","consumer","classic-consumer","subscribe","validation","argument"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}