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
- Read the wrapped cause in the MQClientException - it names the exact line in your selector that threw
- Make the selector defensive: null/size checks on mqs, validate arg type before casting, bound the hash result with Math.floorMod(..., mqs.size())
- Log arg + queue list inside select() during development to catch contract mismatches
- 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
- Never index mqs without bounds-checking; use Math.floorMod
- Validate/cast arg inside select before using it
- Unit-test selectors with empty list, null arg, and min/max queue counts
- Keep selectors pure and fast - no I/O, no external lookups
When it happens
Trigger: triggerScenarios
Common situations: commonSituations
Related errors
- sendSelectImpl call timeout
- select message queue return null.
- 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/ef5650d2568ffef9.
Report an issue: GitHub.