apache/rocketmq · error · MQClientException

Long polling mode, the consumer consumerTimeoutMillisWhenSus

Error message

Long polling mode, the consumer consumerTimeoutMillisWhenSuspend must greater than brokerSuspendMaxTimeMillis

What it means

MQClientException from DefaultLitePullConsumerImpl.checkConfig: in long-polling (lite-pull) mode the consumer must be willing to wait longer than the broker is allowed to suspend the connection — consumerTimeoutMillisWhenSuspend must be >= brokerSuspendMaxTimeMillis. If configured otherwise, the broker could legally hold a poll longer than the consumer waits, so start() rejects the combination.

Source

Thrown at client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultLitePullConsumerImpl.java:444

        // Check messageModel is not null.
        if (null == this.defaultLitePullConsumer.getMessageModel()) {
            throw new MQClientException(
                "messageModel is null"
                    + FAQUrl.suggestTodo(FAQUrl.CLIENT_PARAMETER_CHECK_URL),
                null);
        }

        // Check allocateMessageQueueStrategy is not null
        if (null == this.defaultLitePullConsumer.getAllocateMessageQueueStrategy()) {
            throw new MQClientException(
                "allocateMessageQueueStrategy is null"
                    + FAQUrl.suggestTodo(FAQUrl.CLIENT_PARAMETER_CHECK_URL),
                null);
        }

        if (this.defaultLitePullConsumer.getConsumerTimeoutMillisWhenSuspend() < this.defaultLitePullConsumer.getBrokerSuspendMaxTimeMillis()) {
            throw new MQClientException(
                "Long polling mode, the consumer consumerTimeoutMillisWhenSuspend must greater than brokerSuspendMaxTimeMillis"
                    + FAQUrl.suggestTodo(FAQUrl.CLIENT_PARAMETER_CHECK_URL),
                null);
        }
    }

    public PullAPIWrapper getPullAPIWrapper() {
        return pullAPIWrapper;
    }

    private void startPullTask(Collection<MessageQueue> mqSet) {
        for (MessageQueue messageQueue : mqSet) {
            if (!this.taskTable.containsKey(messageQueue)) {
                PullTaskImpl pullTask = new PullTaskImpl(messageQueue);
                this.taskTable.put(messageQueue, pullTask);
                this.scheduledThreadPoolExecutor.schedule(pullTask, 0, TimeUnit.MILLISECONDS);
            }
        }

View on GitHub (pinned to 293f588571)

Solutions

  1. Keep consumerTimeoutMillisWhenSuspend at least a few seconds above brokerSuspendMaxTimeMillis (e.g. broker 20s -> consumer 25s+)
  2. Or lower brokerSuspendMaxTimeMillis if shorter server-side suspension is acceptable
  3. Revert both to defaults if the tuning was speculative
  4. Document the invariant next to any timeout config in your codebase

Example fix

// before
consumer.setBrokerSuspendMaxTimeMillis(30_000); // raised, consumer timeout still 15s
consumer.start(); // violates invariant

// after
consumer.setBrokerSuspendMaxTimeMillis(30_000);
consumer.setConsumerTimeoutMillisWhenSuspend(35_000);
consumer.start();
Defensive patterns

Strategy: validation

Validate before calling

if (consumer.getConsumerTimeoutMillisWhenSuspend() < consumer.getBrokerSuspendMaxTimeMillis()) {
    consumer.setConsumerTimeoutMillisWhenSuspend(consumer.getBrokerSuspendMaxTimeMillis() + 5_000L);
}

Prevention

When it happens

Trigger: Setting consumerTimeoutMillisWhenSuspend below brokerSuspendMaxTimeMillis (defaults: 15s vs 20s already violates; typical custom breakage: brokerSuspendMaxTimeMillis=30s with default 15s consumer timeout, or consumer timeout lowered to 10s).

Common situations: Tuning timeouts to cut latency without understanding the pairing; increasing brokerSuspendMaxTimeMillis to reduce empty-poll traffic while forgetting the consumer side; copying timeout values between push and lite-pull consumers where semantics differ.

Understand the failure class

Related errors


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