{"id":"00cf0290c5b500b0","repo":"apache/kafka","slug":"consumer-is-not-subscribed-to-any-topics-or-assign-00cf02","errorCode":null,"errorMessage":"Consumer is not subscribed to any topics or assigned any partitions","messagePattern":"Consumer is not subscribed to any topics or assigned any partitions","errorType":"exception","errorClass":"java.lang.IllegalStateException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/consumer/internals/ClassicKafkaConsumer.java","lineNumber":651,"sourceCode":"            release();\n        }\n    }\n\n    @Override\n    public ConsumerRecords<K, V> poll(final Duration timeout) {\n        return poll(time.timer(timeout));\n    }\n\n    /**\n     * @throws KafkaException if the rebalance callback throws exception\n     */\n    private ConsumerRecords<K, V> poll(final Timer timer) {\n        acquireAndEnsureOpen();\n        try {\n            this.kafkaConsumerMetrics.recordPollStart(timer.currentTimeMs());\n\n            if (this.subscriptions.hasNoSubscriptionOrUserAssignment()) {\n                throw new IllegalStateException(\"Consumer is not subscribed to any topics or assigned any partitions\");\n            }\n\n            do {\n                client.maybeTriggerWakeup();\n\n                // try to update assignment metadata BUT do not need to block on the timer for join group\n                updateAssignmentMetadataIfNeeded(timer, false);\n\n                final Fetch<K, V> fetch = pollForFetches(timer);\n                if (!fetch.isEmpty()) {\n                    // before returning the fetched records, we can send off the next round of fetches\n                    // and avoid block waiting for their responses to enable pipelining while the user\n                    // is handling the fetched records.\n                    //\n                    // NOTE: since the consumed position has already been updated, we must not allow\n                    // wakeups or any other errors to be triggered prior to returning the fetched records.\n                    if (sendFetches() > 0 || client.hasPendingRequests()) {\n                        client.transmitSends();","sourceCodeStart":633,"sourceCodeEnd":669,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/consumer/internals/ClassicKafkaConsumer.java#L633-L669","documentation":"Thrown by poll(Timer) when subscriptions.hasNoSubscriptionOrUserAssignment() is true. The consumer must have an active subscription (via subscribe) or a manual assignment (via assign) before it can fetch; otherwise there is nothing to poll and the call would return empty forever. Rejecting early surfaces the misuse as a clear IllegalStateException rather than a silent no-op.","triggerScenarios":"Calling consumer.poll(...) before any consumer.subscribe(...) or consumer.assign(...) call. Calling poll after consumer.unsubscribe() without re-subscribing. Constructing a consumer and immediately polling.","commonSituations":"Startup ordering bug where poll runs before the subscription step. Conditional subscription logic that skips both branches. Cleanup/restart code that unsubscribes but does not re-establish subscription before the next poll loop.","solutions":["Call consumer.subscribe(Collections.singletonList(\"my-topic\")) or consumer.assign(partitions) before the first poll.","After unsubscribe(), re-subscribe or re-assign before the next poll call.","Guard poll in application code with a check that subscription/assignment has been established, especially in restart/reconnect paths."],"exampleFix":"// before\ntry (KafkaConsumer<String,String> c = new KafkaConsumer<>(props)) {\n    ConsumerRecords<String,String> recs = c.poll(Duration.ofMillis(1000));\n}\n\n// after\ntry (KafkaConsumer<String,String> c = new KafkaConsumer<>(props)) {\n    c.subscribe(Collections.singletonList(\"events\"));\n    ConsumerRecords<String,String> recs = c.poll(Duration.ofMillis(1000));\n}","handlingStrategy":"validation","validationCode":"// Guarantee subscription/assignment is set before the first poll\nif (!subscribed && !assigned) {\n    consumer.subscribe(java.util.List.of(\"my-topic\"));\n    // OR: consumer.assign(List.of(new TopicPartition(\"my-topic\", 0)));\n}\norg.apache.kafka.clients.consumer.ConsumerRecords<K,V> records = consumer.poll(java.time.Duration.ofMillis(500));","typeGuard":"// Application-level state flag\nprivate boolean hasSubscriptionOrAssignment() {\n    return subscriptionSet || assignmentSet;\n}\n// Guard poll\nif (!hasSubscriptionOrAssignment()) { throw new IllegalStateException(\n    \"poll() called before subscribe()/assign()\"); }","tryCatchPattern":"try {\n    consumer.poll(timeout);\n} catch (IllegalStateException e) {\n    // Initial poll before subscribe; recover by subscribing then retry\n    consumer.subscribe(java.util.List.of(\"my-topic\"));\n    records = consumer.poll(timeout);\n}","preventionTips":["Call subscribe() or assign() exactly once during consumer setup, before entering the poll loop","Treat 'not subscribed or assigned' as a startup-ordering bug; fix the call order rather than catching it at runtime","Track subscription state in your own flag if you dynamically (un)subscribe"],"tags":["consumer","poll","subscription","illegal-state"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}