{"record":{"id":"ee08dbab21748b3f","repo":"apache/rocketmq","slug":"topic-of-the-message-does-not-match-its-target-mes","errorCode":null,"errorMessage":"Topic of the message does not match its target message queue","messagePattern":"Topic of the message does not match its target message queue","errorType":"validation","errorClass":"MQClientException","httpStatus":null,"severity":"error","filePath":"client/src/main/java/org/apache/rocketmq/client/impl/producer/DefaultMQProducerImpl.java","lineNumber":1278,"sourceCode":"     * @throws RemotingException\n     * @throws InterruptedException\n     * @deprecated It will be removed at 4.4.0 cause for exception handling and the wrong Semantics of timeout. A new one will be\n     * provided in next version\n     */\n    @Deprecated\n    public void send(final Message msg, final MessageQueue mq, final SendCallback sendCallback, final long timeout)\n        throws MQClientException, RemotingException, InterruptedException {\n        BackpressureSendCallBack newCallBack = new BackpressureSendCallBack(sendCallback);\n        final long beginStartTime = System.currentTimeMillis();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    makeSureStateOK();\n                    Validators.checkMessage(msg, defaultMQProducer);\n\n                    if (!msg.getTopic().equals(mq.getTopic())) {\n                        throw new MQClientException(\"Topic of the message does not match its target message queue\", null);\n                    }\n                    long costTime = System.currentTimeMillis() - beginStartTime;\n                    if (timeout > costTime) {\n                        try {\n                            sendKernelImpl(msg, mq, CommunicationMode.ASYNC, newCallBack, null,\n                                timeout - costTime);\n                        } catch (MQBrokerException e) {\n                            throw new MQClientException(\"unknown exception\", e);\n                        }\n                    } else {\n                        newCallBack.onException(new RemotingTooMuchRequestException(\"call timeout\"));\n                    }\n                } catch (Exception e) {\n                    newCallBack.onException(e);\n                }\n            }\n\n        };","sourceCodeStart":1260,"sourceCodeEnd":1296,"githubUrl":"https://github.com/apache/rocketmq/blob/293f5885719fc4aa3619446a1900f58ccfcfdd29/client/src/main/java/org/apache/rocketmq/client/impl/producer/DefaultMQProducerImpl.java#L1260-L1296","documentation":"Thrown by the async send overload that targets a specific MessageQueue when msg.getTopic() does not equal mq.getTopic(). The producer refuses the call because routing a message to a queue belonging to another topic would put the message in the wrong destination on the broker. It is a pure client-side precondition check performed before sendKernelImpl.","triggerScenarios":"Calling producer.send(msg, mq, sendCallback, timeout) where the MessageQueue was fetched from a different topic's TopicPublishInfo, or where the message topic was changed (e.g. namespace applied to one side but not the other) after the queue was selected.","commonSituations":"Caching MessageQueue instances across topics; building queues by hand (new MessageQueue(topic, broker, queueId)) with a topic string that differs from the message (including %RETRY% / namespace prefixes); refactoring code that selects the queue from topic A but sends topic B.","solutions":["Verify that the MessageQueue passed to send() was obtained from route data for the exact same topic string as msg.getTopic() (including namespace prefix).","If you construct queues manually, set the queue's topic from msg.getTopic() instead of a hard-coded constant.","Fetch queues fresh via producer.fetchPublishMessageQueues(topic) per topic right before sending instead of reusing cached instances.","Check that namespace handling (ClientConfig#queueWithNamespace / NamespaceUtil) is not applied twice or skipped on one side, causing '%namespace%topic' vs 'topic' mismatch."],"exampleFix":"// before\nMessageQueue mq = cachedQueues.get(\"otherTopic\");\nproducer.send(msg, mq, callback, 3000);\n\n// after\nList<MessageQueue> mqs = producer.fetchPublishMessageQueues(msg.getTopic());\nMessageQueue mq = mqs.get(queueId);\nproducer.send(msg, mq, callback, 3000);","handlingStrategy":"validation","validationCode":"// before calling send(msg, mq, callback, timeout)\nif (mq == null || !msg.getTopic().equals(mq.getTopic())) {\n    throw new IllegalArgumentException(\"queue topic '\" + (mq == null ? null : mq.getTopic())\n        + \"' != message topic '\" + msg.getTopic() + \"'\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    producer.send(msg, mq, callback, timeout);\n} catch (MQClientException e) {\n    if (e.getMessage().contains(\"does not match its target message queue\")) {\n        mq = producer.fetchPublishMessageQueues(msg.getTopic()).get(0); // re-bind queue to topic\n        producer.send(msg, mq, callback, timeout);\n    } else throw e;\n}","preventionTips":["Always obtain the MessageQueue from route data of the same message being sent.","Never cache MessageQueue instances keyed by anything other than their own topic.","When using namespaces, apply namespace handling in exactly one place (client config), not manually on messages."],"tags":["rocketmq","producer","message-queue","topic-mismatch","async-send"],"backgroundTag":null,"analyzedSha":"293f5885719fc4aa3619446a1900f58ccfcfdd29","analyzedAt":"2026-08-14T11:50:13.822Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}