{"record":{"id":"d30268d4300e5a8e","repo":"apache/rocketmq","slug":"subscribe-and-assign-are-mutually-exclusive","errorCode":null,"errorMessage":"Subscribe and assign are mutually exclusive.","messagePattern":"Subscribe and assign are mutually exclusive\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultLitePullConsumerImpl.java","lineNumber":214,"sourceCode":"            }\n        }\n    }\n\n    private void checkServiceState() {\n        if (this.serviceState != ServiceState.RUNNING) {\n            throw new IllegalStateException(NOT_RUNNING_EXCEPTION_MESSAGE);\n        }\n    }\n\n    public void updateNameServerAddr(String newAddresses) {\n        this.mQClientFactory.getMQClientAPIImpl().updateNameServerAddressList(newAddresses);\n    }\n\n    private synchronized void setSubscriptionType(SubscriptionType type) {\n        if (this.subscriptionType == SubscriptionType.NONE) {\n            this.subscriptionType = type;\n        } else if (this.subscriptionType != type) {\n            throw new IllegalStateException(SUBSCRIPTION_CONFLICT_EXCEPTION_MESSAGE);\n        }\n    }\n\n    private void updateAssignedMessageQueue(String topic, Set<MessageQueue> assignedMessageQueue) {\n        this.assignedMessageQueue.updateAssignedMessageQueue(topic, assignedMessageQueue);\n    }\n\n    private void updatePullTask(String topic, Set<MessageQueue> mqNewSet) {\n        Iterator<Map.Entry<MessageQueue, PullTaskImpl>> it = this.taskTable.entrySet().iterator();\n        while (it.hasNext()) {\n            Map.Entry<MessageQueue, PullTaskImpl> next = it.next();\n            if (next.getKey().getTopic().equals(topic)) {\n                if (!mqNewSet.contains(next.getKey())) {\n                    next.getValue().setCancelled(true);\n                    it.remove();\n                }\n            }\n        }","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/apache/rocketmq/blob/293f5885719fc4aa3619446a1900f58ccfcfdd29/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultLitePullConsumerImpl.java#L196-L232","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (one instance, both modes)\nconsumer.subscribe(\"T\", \"*\");\nconsumer.assign(Arrays.asList(mq0, mq1)); // IllegalStateException\n\n// after (dedicated instances)\nDefaultLitePullConsumer sub = ...; sub.subscribe(\"T\", \"*\");\nDefaultLitePullConsumer manual = ...; manual.assign(Arrays.asList(mq0, mq1));","handlingStrategy":"validation","validationCode":"// enforce one mode per instance in your wrapper\nprivate final Set<Mode> used = EnumSet.noneOf(Mode.class);\npublic void subscribeOrAssign(Mode m, Runnable r) {\n    if (!used.isEmpty() && !used.contains(m)) {\n        throw new IllegalStateException(\"Consumer already in \" + used + \" mode\");\n    }\n    used.add(m); r.run();\n}","typeGuard":null,"tryCatchPattern":"try {\n    consumer.assign(queues);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"mutually exclusive\")) {\n        // switch to a fresh consumer instance for assigned mode\n    } else throw e;\n}","preventionTips":["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"],"tags":["rocketmq","consumer","lite-pull","api-misuse","lifecycle"],"backgroundTag":null,"analyzedSha":"293f5885719fc4aa3619446a1900f58ccfcfdd29","analyzedAt":"2026-08-14T11:50:13.822Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}