apache/rocketmq · error · MQClientException

select message queue threw exception.

Error message

select message queue threw exception.

What it means

MQClientException thrown from invokeMessageQueueSelector when user-supplied MessageQueueSelector.select(...) (or the queue parsing / namespace handling around it) throws any Throwable. The client catches Throwable deliberately so a broken selector never silently skips selection, wraps the original exception as the cause, and aborts the send-select call. The stack trace of the cause contains the real error in your selector code.

Source

Thrown at client/src/main/java/org/apache/rocketmq/client/impl/producer/DefaultMQProducerImpl.java:702

    public MessageQueue invokeMessageQueueSelector(Message msg, MessageQueueSelector selector, Object arg,
                                                   final long timeout) throws MQClientException, RemotingTooMuchRequestException {
        long beginStartTime = System.currentTimeMillis();
        this.makeSureStateOK();
        Validators.checkMessage(msg, this.defaultMQProducer);

        TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
        if (topicPublishInfo != null && topicPublishInfo.ok()) {
            MessageQueue mq = null;
            try {
                List<MessageQueue> messageQueueList =
                        mQClientFactory.getMQAdminImpl().parsePublishMessageQueues(topicPublishInfo.getMessageQueueList());
                Message userMessage = MessageAccessor.cloneMessage(msg);
                String userTopic = NamespaceUtil.withoutNamespace(userMessage.getTopic(), mQClientFactory.getClientConfig().getNamespace());
                userMessage.setTopic(userTopic);

                mq = mQClientFactory.getClientConfig().queueWithNamespace(selector.select(messageQueueList, userMessage, arg));
            } catch (Throwable e) {
                throw new MQClientException("select message queue threw exception.", e);
            }

            long costTime = System.currentTimeMillis() - beginStartTime;
            if (timeout < costTime) {
                throw new RemotingTooMuchRequestException("sendSelectImpl call timeout");
            }
            if (mq != null) {
                return mq;
            } else {
                throw new MQClientException("select message queue return null.", null);
            }
        }

        validateNameServerSetting();
        throw new MQClientException("No route info for this topic, " + msg.getTopic(), null);
    }

    public MessageQueue selectOneMessageQueue(final TopicPublishInfo tpInfo, final String lastBrokerName, final boolean resetIndex) {

View on GitHub (pinned to 293f588571)

Solutions

  1. Read the wrapped cause in the MQClientException - it names the exact line in your selector that threw
  2. Make the selector defensive: null/size checks on mqs, validate arg type before casting, bound the hash result with Math.floorMod(..., mqs.size())
  3. Log arg + queue list inside select() during development to catch contract mismatches
  4. Add a unit test invoking the selector with the same arg types and a 1-queue list, the minimal failure case

Example fix

// before
MessageQueueSelector s = (mqs, m, arg) -> mqs.get((Integer) arg); // ClassCastException/NPE risk
// after
MessageQueueSelector s = (mqs, m, arg) -> {
    if (mqs == null || mqs.isEmpty() || arg == null) return null;
    int i = Math.floorMod(((Number) arg).intValue(), mqs.size());
    return mqs.get(i);
};
Defensive patterns

Strategy: try-catch

Validate before calling

 // exercise selector logic before wiring it in
List<MessageQueue> mqs = producer.fetchPublishMessageQueues(topic);
MessageQueue mq = selector.select(mqs, sampleMessage, sampleArg);
Objects.requireNonNull(mq, "selector must return a queue");

Type guard

 MessageQueueSelector safeSelector = (mqs, m, arg) -> {
    if (mqs == null || mqs.isEmpty() || arg == null) {
        throw new IllegalArgumentException("bad selector input");
    }
    return mqs.get(Math.floorMod(((Number) arg).intValue(), mqs.size()));
};

Try / catch

try {
    producer.send(msg, selector, arg);
} catch (MQClientException e) {
    if ("select message queue threw exception.".equals(e.getMessage()) && e.getCause() != null) {
        log.error("Selector bug", e.getCause()); // real stack trace is in the cause
    }
    throw e;
}

Prevention

When it happens

Trigger: triggerScenarios

Common situations: commonSituations

Related errors


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