apache/rocketmq · error · MQClientException
Topic of the message does not match its target message queue
Error message
Topic of the message does not match its target message queue
What it means
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.
Source
Thrown at client/src/main/java/org/apache/rocketmq/client/impl/producer/DefaultMQProducerImpl.java:1278
* @throws RemotingException
* @throws InterruptedException
* @deprecated It will be removed at 4.4.0 cause for exception handling and the wrong Semantics of timeout. A new one will be
* provided in next version
*/
@Deprecated
public void send(final Message msg, final MessageQueue mq, final SendCallback sendCallback, final long timeout)
throws MQClientException, RemotingException, InterruptedException {
BackpressureSendCallBack newCallBack = new BackpressureSendCallBack(sendCallback);
final long beginStartTime = System.currentTimeMillis();
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
makeSureStateOK();
Validators.checkMessage(msg, defaultMQProducer);
if (!msg.getTopic().equals(mq.getTopic())) {
throw new MQClientException("Topic of the message does not match its target message queue", null);
}
long costTime = System.currentTimeMillis() - beginStartTime;
if (timeout > costTime) {
try {
sendKernelImpl(msg, mq, CommunicationMode.ASYNC, newCallBack, null,
timeout - costTime);
} catch (MQBrokerException e) {
throw new MQClientException("unknown exception", e);
}
} else {
newCallBack.onException(new RemotingTooMuchRequestException("call timeout"));
}
} catch (Exception e) {
newCallBack.onException(e);
}
}
};View on GitHub (pinned to 293f588571)
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.
Example fix
// before
MessageQueue mq = cachedQueues.get("otherTopic");
producer.send(msg, mq, callback, 3000);
// after
List<MessageQueue> mqs = producer.fetchPublishMessageQueues(msg.getTopic());
MessageQueue mq = mqs.get(queueId);
producer.send(msg, mq, callback, 3000); Defensive patterns
Strategy: validation
Validate before calling
// before calling send(msg, mq, callback, timeout)
if (mq == null || !msg.getTopic().equals(mq.getTopic())) {
throw new IllegalArgumentException("queue topic '" + (mq == null ? null : mq.getTopic())
+ "' != message topic '" + msg.getTopic() + "'");
} Try / catch
try {
producer.send(msg, mq, callback, timeout);
} catch (MQClientException e) {
if (e.getMessage().contains("does not match its target message queue")) {
mq = producer.fetchPublishMessageQueues(msg.getTopic()).get(0); // re-bind queue to topic
producer.send(msg, mq, callback, timeout);
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- message's topic not equal mq's topic
- sendKernelImpl call timeout
- 13
- Sending message to topic[%s] is forbidden.
- sendMessage call timeout
AI-assisted analysis of apache/rocketmq@293f588571 (2026-08-14).
Data as JSON: /api/errors/ee08dbab21748b3f.
Report an issue: GitHub.