apache/rocketmq · error · MQClientException

executor rejected

Error message

executor rejected 

What it means

MQClientException thrown from the async-send path when executor.submit(runnable) raises RejectedExecutionException and backpressure is disabled. The default async executor has a bounded queue; once the in-flight async sends exceed it, the JDK executor rejects the task and RocketMQ rethrows it as this error. When enableBackpressureForAsyncMode is true the rejection is instead absorbed by running the task on the caller thread (natural backpressure).

Source

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

                costTime = System.currentTimeMillis() - beginStartTime;

                isSemaphoreAsyncSizeAcquired = timeout - costTime > 0
                    && semaphoreAsyncSendSize.tryAcquire(msgLen, timeout - costTime, TimeUnit.MILLISECONDS);
                sendCallback.isSemaphoreAsyncSizeAcquired = isSemaphoreAsyncSizeAcquired;
                defaultMQProducer.releaseBackPressureForAsyncSendSizeLock();
                if (!isSemaphoreAsyncSizeAcquired) {
                    sendCallback.onException(
                        new RemotingTooMuchRequestException("send message tryAcquire semaphoreAsyncSize timeout"));
                    return;
                }
            }

            executor.submit(runnable);
        } catch (RejectedExecutionException e) {
            if (isEnableBackpressureForAsyncMode) {
                runnable.run();
            } else {
                throw new MQClientException("executor rejected ", e);
            }
        }
    }

    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());

View on GitHub (pinned to 293f588571)

Solutions

  1. Enable backpressure so rejection degrades to caller-thread execution: producer.setEnableBackpressureForAsyncMode(true) (and tune backpressureForAsyncFlushTimeout if needed)
  2. Increase the async executor queue capacity via producer.setAsyncExecutorSizeConfig(...) / defaultAsyncExecutorQueueSize to match peak throughput
  3. Rate-limit or batch sends at the application layer (semaphore, Resilience4j bulkhead) so in-flight async sends stay under the queue bound
  4. Ensure SendCallback implementations return quickly; offload heavy work to a separate executor

Example fix

// before
producer.setEnableBackpressureForAsyncMode(false);
producer.send(msg, callback); // burst -> executor rejected
// after
producer.setEnableBackpressureForAsyncMode(true);
producer.send(msg, callback); // overflow runs on caller thread instead of throwing
Defensive patterns

Strategy: fallback

Validate before calling

 producer.setEnableBackpressureForAsyncMode(true); // rejection degrades to caller-thread run
// optional: size the executor for peak throughput
producer.getDefaultMQProducerImpl()... // or set asyncExecutorSizeConfig queue size >= peak in-flight sends

Try / catch

producer.send(msg, new SendCallback() {
    public void onSuccess(SendResult r) { ... }
    public void onException(Throwable e) {
        if (e instanceof MQClientException && String.valueOf(e.getMessage()).contains("executor rejected")) {
            localBuffer.offer(msg); // fallback: spool and retry later
        }
    }
});

Prevention

When it happens

Trigger: triggerScenarios

Common situations: See trigger scenarios.

Related errors


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