{"id":"c839c6b7d2572275","repo":"apache/kafka","slug":"topic-must-be-non-null","errorCode":null,"errorMessage":"Topic must be non-null.","messagePattern":"Topic must be non-null\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/consumer/ConsumerRecords.java","lineNumber":122,"sourceCode":"            // every call. A time based approach is used to avoid this. See KAFKA-20660 for more details.\n            if (now - lastLog >= TAINT_LOG_INTERVAL_NS && TAINTED_NEXT_OFFSETS_LAST_LOG_NS.compareAndSet(lastLog, now)) {\n                log.error(\"ConsumerRecords#nextOffsets() returned empty because this instance was built with the \" +\n                        \"deprecated ConsumerRecords(Map) constructor (see KIP-1094), which does not supply next offsets. \" +\n                        \"Downstream logic that relies on these offsets to advance the consumer's committed position \" +\n                        \"(for example, Kafka Streams under exactly-once semantics) will be unable to commit, leading to \" +\n                        \"reprocessing. Update the interceptor or wrapper that constructed it to use the \" +\n                        \"ConsumerRecords(Map, Map) constructor that supplies next offsets.\");\n            }\n        }\n        return nextOffsets;\n    }\n\n    /**\n     * Get just the records for the given topic\n     */\n    public Iterable<ConsumerRecord<K, V>> records(String topic) {\n        if (topic == null)\n            throw new IllegalArgumentException(\"Topic must be non-null.\");\n        List<List<ConsumerRecord<K, V>>> recs = new ArrayList<>();\n        for (Map.Entry<TopicPartition, List<ConsumerRecord<K, V>>> entry : records.entrySet()) {\n            if (entry.getKey().topic().equals(topic))\n                recs.add(entry.getValue());\n        }\n        return new ConcatenatedIterable<>(recs);\n    }\n\n    /**\n     * Get the partitions which have records contained in this record set.\n     * @return The set of partitions with data in this record set (may be empty if no data was returned)\n     */\n    public Set<TopicPartition> partitions() {\n        return Collections.unmodifiableSet(records.keySet());\n    }\n\n    @Override\n    public Iterator<ConsumerRecord<K, V>> iterator() {","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/consumer/ConsumerRecords.java#L104-L140","documentation":"IllegalArgumentException thrown by ConsumerRecords.records(String topic) when the topic argument is null. The lookup compares each partition's topic via equals(), so a null topic would never match and is treated as a programming error.","triggerScenarios":"Calling consumerRecords.records(topicVar) where topicVar is null; passing a topic sourced from an Optional/Map.get that returned null without a guard.","commonSituations":"Topic name read from external config that is missing; code branching on a topic string that was never set; loop variables that resolve to null for empty iteration.","solutions":["Validate the topic variable is non-null before calling records(topic).","Ensure the source feeding the topic name (config, request param, map key) actually contains it.","Default or skip with an explicit empty check: if (topic != null) { for (Record r : cr.records(topic)) ... }."],"exampleFix":"// before\nIterable<ConsumerRecord<K,V>> recs = consumerRecords.records(maybeTopic);\n\n// after\nif (maybeTopic == null) throw new IllegalStateException(\"topic not configured\");\nIterable<ConsumerRecord<K,V>> recs = consumerRecords.records(maybeTopic);","handlingStrategy":"validation","validationCode":"if (topic == null) {\n    throw new IllegalArgumentException(\"topic must be non-null for ConsumerRecords.records(topic)\");\n}\nIterable<ConsumerRecord<K,V>> recs = consumerRecords.records(topic);","typeGuard":null,"tryCatchPattern":"try {\n    consumerRecords.records(topic);\n} catch (IllegalArgumentException e) {\n    if (\"Topic must be non-null.\".equals(e.getMessage())) {\n        // topic came from external input — default to iterating consumerRecords.partitions() instead\n    }\n    throw e;\n}","preventionTips":["Prefer iterating `consumerRecords.partitions()` and filtering by TopicPartition when the topic source is untrusted.","Null-check any topic variable sourced from request params, configs, or maps before calling .records(topic).","Wrap ConsumerRecords usage in a helper that rejects null topics at the boundary."],"tags":["consumer","consumer-records","null-check","api-usage"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}