apache/rocketmq · error · IllegalStateException
Subscribe and assign are mutually exclusive.
Error message
Subscribe and assign are mutually exclusive.
What it means
IllegalStateException thrown by DefaultLitePullConsumerImpl.setSubscriptionType: a DefaultLitePullConsumer supports exactly one consumption mode per lifetime — subscription-based (subscribe()) or manual assignment (assign()). The first call fixes the type (SUBSCRIBE or ASSIGN); a later call of the opposite type throws this.
Source
Thrown at client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultLitePullConsumerImpl.java:214
}
}
}
private void checkServiceState() {
if (this.serviceState != ServiceState.RUNNING) {
throw new IllegalStateException(NOT_RUNNING_EXCEPTION_MESSAGE);
}
}
public void updateNameServerAddr(String newAddresses) {
this.mQClientFactory.getMQClientAPIImpl().updateNameServerAddressList(newAddresses);
}
private synchronized void setSubscriptionType(SubscriptionType type) {
if (this.subscriptionType == SubscriptionType.NONE) {
this.subscriptionType = type;
} else if (this.subscriptionType != type) {
throw new IllegalStateException(SUBSCRIPTION_CONFLICT_EXCEPTION_MESSAGE);
}
}
private void updateAssignedMessageQueue(String topic, Set<MessageQueue> assignedMessageQueue) {
this.assignedMessageQueue.updateAssignedMessageQueue(topic, assignedMessageQueue);
}
private void updatePullTask(String topic, Set<MessageQueue> mqNewSet) {
Iterator<Map.Entry<MessageQueue, PullTaskImpl>> it = this.taskTable.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<MessageQueue, PullTaskImpl> next = it.next();
if (next.getKey().getTopic().equals(topic)) {
if (!mqNewSet.contains(next.getKey())) {
next.getValue().setCancelled(true);
it.remove();
}
}
}View on GitHub (pinned to 293f588571)
Solutions
- Pick one mode per consumer instance: subscribe() for rebalanced group consumption, assign() for manual queue control
- Create a separate DefaultLitePullConsumer instance if you need both patterns
- Remove leftover subscribe/assign calls left by refactoring
- Restart with a fresh instance if the conflict comes from earlier experiments in the same process
Example fix
// before (one instance, both modes)
consumer.subscribe("T", "*");
consumer.assign(Arrays.asList(mq0, mq1)); // IllegalStateException
// after (dedicated instances)
DefaultLitePullConsumer sub = ...; sub.subscribe("T", "*");
DefaultLitePullConsumer manual = ...; manual.assign(Arrays.asList(mq0, mq1)); Defensive patterns
Strategy: validation
Validate before calling
// enforce one mode per instance in your wrapper
private final Set<Mode> used = EnumSet.noneOf(Mode.class);
public void subscribeOrAssign(Mode m, Runnable r) {
if (!used.isEmpty() && !used.contains(m)) {
throw new IllegalStateException("Consumer already in " + used + " mode");
}
used.add(m); r.run();
} Try / catch
try {
consumer.assign(queues);
} catch (IllegalStateException e) {
if (e.getMessage().contains("mutually exclusive")) {
// switch to a fresh consumer instance for assigned mode
} else throw e;
} Prevention
- Decide subscribe-vs-assign at design time per consumer bean
- Use one consumer instance per consumption mode
- Clean out dead code paths mixing both APIs
When it happens
Trigger: Calling consumer.assign(...) after consumer.subscribe(...) (or vice versa) on the same instance. Each assign()/subscribe() call routes through setSubscriptionType, so mixing them in any order on one consumer triggers the conflict.
Common situations: Copy-pasting samples that use both APIs; refactoring from subscribe to assign without a new instance; utility code that 'tops up' subscriptions with assigns; reusing a consumer bean for different consumption patterns via configuration switches.
Related errors
- The consumer not running, please start it first.
- The PullConsumer service state not OK, maybe started once,
- consumerGroup can not equal
- messageModel is null
- allocateMessageQueueStrategy is null
AI-assisted analysis of apache/rocketmq@293f588571 (2026-08-14).
Data as JSON: /api/errors/d30268d4300e5a8e.
Report an issue: GitHub.