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

  1. Verify that the MessageQueue passed to send() was obtained from route data for the exact same topic string as msg.getTopic() (including namespace prefix).
  2. If you construct queues manually, set the queue's topic from msg.getTopic() instead of a hard-coded constant.
  3. Fetch queues fresh via producer.fetchPublishMessageQueues(topic) per topic right before sending instead of reusing cached instances.
  4. 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

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


AI-assisted analysis of apache/rocketmq@293f588571 (2026-08-14). Data as JSON: /api/errors/ee08dbab21748b3f. Report an issue: GitHub.