{"id":"5170e2fceb970530","repo":"apache/kafka","slug":"consumer-is-not-subscribed-to-any-topics-or-assign","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/AsyncKafkaConsumer.java","lineNumber":938,"sourceCode":"     *             partitions to consume from or an unexpected error occurred\n     * @throws org.apache.kafka.clients.consumer.OffsetOutOfRangeException if the fetch position of the consumer is\n     *             out of range and no offset reset policy is configured.\n     * @throws org.apache.kafka.common.errors.TopicAuthorizationException if the consumer is not authorized to read\n     *             from a partition\n     * @throws org.apache.kafka.common.errors.SerializationException if the fetched records cannot be deserialized\n     * @throws org.apache.kafka.common.errors.UnsupportedAssignorException if the `group.remote.assignor` configuration\n     *             is set to an assignor that is not available on the broker.\n     */\n    @Override\n    public ConsumerRecords<K, V> poll(final Duration timeout) {\n        Timer timer = time.timer(timeout);\n\n        acquireAndEnsureOpen();\n        try {\n            kafkaConsumerMetrics.recordPollStart(timer.currentTimeMs());\n\n            if (subscriptions.hasNoSubscriptionOrUserAssignment()) {\n                throw new IllegalStateException(\"Consumer is not subscribed to any topics or assigned any partitions\");\n            }\n\n            // This distinguishes the first pass of the inner do/while loop from subsequent passes for the\n            // inflight poll event logic.\n            boolean firstPass = true;\n\n            do {\n                // We must not allow wake-ups between polling for fetches and returning the records.\n                // If the polled fetches are not empty the consumed position has already been updated in the polling\n                // of the fetches. A wakeup between returned fetches and returning records would lead to never\n                // returning the records in the fetches. Thus, we trigger a possible wake-up before we poll fetches.\n                wakeupTrigger.maybeTriggerWakeup();\n\n                checkInflightPoll(timer, firstPass);\n                firstPass = false;\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","sourceCodeStart":920,"sourceCodeEnd":956,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/consumer/internals/AsyncKafkaConsumer.java#L920-L956","documentation":"IllegalStateException thrown by AsyncKafkaConsumer.poll at the top of the method when subscriptions.hasNoSubscriptionOrUserAssignment() is true. poll() cannot return records without an active source of partitions, so calling it before subscribe() or assign() is treated as a usage bug. This is a fast-fail guard rather than returning empty records indefinitely, surfacing the wiring mistake at the call site.","triggerScenarios":"Invoking consumer.poll(timeout) before any consumer.subscribe(...) or consumer.assign(...) call. Also possible if subscription was cleared (unsubscribe) and poll is invoked again without re-subscribing, or if the subscribe call silently failed (e.g. an empty topic collection).","commonSituations":"Application startup ordering bug where poll runs in a background thread before the subscription step completes; refactoring that moves subscribe() into a conditional branch not always taken; subscribe(Collections.emptyList()) followed by poll; consumer reused after unsubscribe() without re-subscribing; race between main thread subscribing and a health-check thread polling.","solutions":["Call consumer.subscribe(topics) or consumer.assign(partitions) before the first consumer.poll(...).","Ensure subscribe is called with a non-empty topic collection and that any regex Pattern actually matches at least one topic.","If you previously called unsubscribe(), re-subscribe or re-assign before polling again.","Guard startup ordering so the polling thread waits on a readiness signal until subscription is established."],"exampleFix":"// before\ntry (var consumer = new KafkaConsumer<String,String>(props)) {\n    ConsumerRecords<String,String> records = consumer.poll(Duration.ofMillis(100));\n}\n\n// after\ntry (var consumer = new KafkaConsumer<String,String>(props)) {\n    consumer.subscribe(List.of(\"orders\"));\n    ConsumerRecords<String,String> records = consumer.poll(Duration.ofMillis(100));\n}","handlingStrategy":"validation","validationCode":"// Guard poll() with an explicit readiness check before the first call,\n// and again whenever subscription may have been revoked.\nstatic void ensureReadyForPoll(Consumer<?, ?> c) {\n    if (c.subscription().isEmpty() && c.assignment().isEmpty()) {\n        throw new IllegalStateException(\n            \"Consumer has neither an active subscription nor a manual assignment; \" +\n            \"call subscribe() or assign() before poll().\");\n    }\n}\n\n// Usage:\n//   consumer.subscribe(singleton(\"orders\"));   // or consumer.assign(partitions)\n//   ensureReadyForPoll(consumer);\n//   ConsumerRecords<K,V> records = consumer.poll(Duration.ofMillis(500));\n//\n// For the async consumer, prefer checking c.subscription()/c.assignment() over\n// keeping your own flag — they reflect rebalances that may have revoked partitions.","typeGuard":null,"tryCatchPattern":"// IllegalStateException from poll() indicates a programmer error, not a transient\n// condition. Do NOT retry blindly; fix the subscription state.\ntry {\n    ensureReadyForPoll(consumer);                 // cheap pre-check\n    records = consumer.poll(Duration.ofMillis(500));\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"not subscribed\")) {\n        log.warn(\"No subscription/assignment; re-subscribing before next poll cycle\");\n        consumer.subscribe(topics, listener);     // recover state, then continue\n        // skip this poll, try again next loop iteration\n    } else {\n        throw e;                                  // different IllegalStateException — propagate\n    }\n}","preventionTips":["Always pair subscribe() or assign() with the first poll(); never poll in a code path that may have skipped subscription (e.g. an early-return branch).","In a rebalance callback (ConsumerRebalanceListener.onPartitionsRevoked), do not assume partitions remain assigned after the callback returns; re-check before the next poll if your logic depends on them.","Centralize your poll loop in one method so the subscribe/assign precondition is guaranteed at every call site.","Treat 'Consumer is not subscribed' as a bug in your wiring, not a runtime hiccup — log a stack trace and fix the code path."],"tags":["consumer","poll","subscription","usage"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}