apache/kafka · error · IllegalStateException

Subscription to topics, partitions and pattern are mutually

Error message

Subscription to topics, partitions and pattern are mutually exclusive

What it means

Thrown by SubscriptionState.setSubscriptionType when the consumer's subscription type has already been set to a different mode (AUTO_TOPICS, AUTO_PATTERN, AUTO_PATTERN_RE2J, or manual assignment). Kafka enforces that a consumer uses exactly one subscription mechanism; mixing subscribe(topics), subscribe(pattern), and assign(partitions) on the same instance is illegal and the state machine rejects it with this message.

Source

Thrown at clients/src/main/java/org/apache/kafka/clients/consumer/internals/SubscriptionState.java:189

     * be used to check when an assignment has changed.
     *
     * @return The current assignment Id
     */
    synchronized int assignmentId() {
        return assignmentId;
    }

    /**
     * This method sets the subscription type if it is not already set (i.e. when it is NONE),
     * or verifies that the subscription type is equal to the give type when it is set (i.e.
     * when it is not NONE)
     * @param type The given subscription type
     */
    private void setSubscriptionType(SubscriptionType type) {
        if (this.subscriptionType == SubscriptionType.NONE)
            this.subscriptionType = type;
        else if (this.subscriptionType != type)
            throw new IllegalStateException(SUBSCRIPTION_EXCEPTION_MESSAGE);
    }

    public synchronized boolean subscribe(Set<String> topics, Optional<ConsumerRebalanceListener> listener) {
        registerRebalanceListener(listener);
        setSubscriptionType(SubscriptionType.AUTO_TOPICS);
        return changeSubscription(topics);
    }

    public synchronized void subscribe(Pattern pattern, Optional<ConsumerRebalanceListener> listener) {
        registerRebalanceListener(listener);
        setSubscriptionType(SubscriptionType.AUTO_PATTERN);
        this.subscribedPattern = pattern;
    }

    public synchronized void subscribe(SubscriptionPattern pattern, Optional<ConsumerRebalanceListener> listener) {
        registerRebalanceListener(listener);
        setSubscriptionType(SubscriptionType.AUTO_PATTERN_RE2J);
        this.subscribedRe2JPattern = pattern;

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Pick one subscription mode (topics, pattern, or manual assign) and use only that for the lifetime of the consumer instance.
  2. If the subscription mode must change, close() the consumer and create a new instance.
  3. Audit framework/wrapper code that may call subscribe internally before your application calls assign (or vice versa).
  4. For pattern subscriptions, ensure you are not also passing a topic list elsewhere in setup.

Example fix

// before
consumer.subscribe(Arrays.asList("orders"));
consumer.assign(Collections.singletonList(new TopicPartition("orders", 0)));  // throws

// after
// option A: topic subscription only
consumer.subscribe(Arrays.asList("orders"));
// option B: manual assignment only (new instance)
consumer.close();
consumer = new KafkaConsumer<>(props);
consumer.assign(Collections.singletonList(new TopicPartition("orders", 0)));
Defensive patterns

Strategy: validation

Validate before calling

// Enforce one subscription mode per consumer instance at construction time.
public enum SubscriptionMode { TOPICS, PATTERN, ASSIGNED }
private final SubscriptionMode mode;

public ShareConsumerRunner(SubProperties props, SubscriptionMode mode) {
    this.mode = Objects.requireNonNull(mode);
}

void start() {
    switch (mode) {
        case TOPICS    -> consumer.subscribe(props.topics);
        case PATTERN   -> consumer.subscribe(props.pattern);
        case ASSIGNED  -> consumer.assign(props.partitions);
    }
    // No code path may call a second subscription method on this instance.
}

Try / catch

try {
    consumer.subscribe(topics);
} catch (IllegalStateException e) {
    if (e.getMessage().contains("mutually exclusive")) {
        // The consumer was already subscribed/assigned via a different API.
        // Close it and create a fresh consumer with a single subscription mode.
        log.error("Conflicting subscription on same consumer; recreating", e);
        consumer.close();
        consumer = new KafkaConsumer<>(props);
        consumer.subscribe(topics);
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling consumer.subscribe(Collection<String>) after consumer.subscribe(Pattern), or consumer.assign(...) after subscribe(...), or any combination of the three on the same KafkaConsumer/KafkaShareConsumer instance. Each call routes through setSubscriptionType, which throws if the prior type differs.

Common situations: Refactoring a consumer from topic list to pattern (or vice versa) without recreating the consumer; library/framework code that calls subscribe then user code calls assign; mixing manual assignment with group subscription in test harnesses; migrating between share and regular consumers and reusing the instance.

Related errors


AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03). Data as JSON: /data/errors/3d6b106ae4e8504a.json. Report an issue: GitHub.