apache/rocketmq · error · IllegalArgumentException

Message queues can not be null or empty.

Error message

Message queues can not be null or empty.

What it means

DefaultLitePullConsumerImpl.assign(Collection<MessageQueue>) throws this IllegalArgumentException when the passed collection is null or empty. In ASSIGN mode the consumer pulls exclusively from the queues you explicitly assign, so an empty assignment leaves it nothing to consume and is rejected outright.

Source

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

            assignedMessageQueue.setRebalanceImpl(this.rebalanceImpl);
            if (serviceState == ServiceState.RUNNING) {
                this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();
                updateTopicSubscribeInfoWhenSubscriptionChanged();
            }
        } catch (Exception e) {
            throw new MQClientException("subscribe exception", e);
        }
    }

    public synchronized void unsubscribe(final String topic) {
        this.rebalanceImpl.getSubscriptionInner().remove(topic);
        removePullTaskCallback(topic);
        assignedMessageQueue.removeAssignedMessageQueue(topic);
    }

    public synchronized void assign(Collection<MessageQueue> messageQueues) {
        if (messageQueues == null || messageQueues.isEmpty()) {
            throw new IllegalArgumentException("Message queues can not be null or empty.");
        }
        setSubscriptionType(SubscriptionType.ASSIGN);
        assignedMessageQueue.updateAssignedMessageQueue(messageQueues);
        if (serviceState == ServiceState.RUNNING) {
            updateAssignPullTask(messageQueues);
        }
    }

    public synchronized void setSubExpressionForAssign(final String topic, final String subExpression) {
        if (StringUtils.isBlank(subExpression)) {
            throw new IllegalArgumentException("subExpression can not be null or empty.");
        }
        if (serviceState != ServiceState.CREATE_JUST) {
            throw new IllegalStateException("setAssignTag only can be called before start.");
        }
        setSubscriptionType(SubscriptionType.ASSIGN);
        topicToSubExpression.put(topic, subExpression);
    }

View on GitHub (pinned to 293f588571)

Solutions

  1. Guard: only call assign when the collection is non-null and !isEmpty()
  2. If the list came from fetchMessageQueues, wait/retry until topic route metadata returns queues
  3. Fall back to subscribe() mode if dynamic queue discovery is what you actually need

Example fix

// before
consumer.assign(queues.stream().filter(q -> q.getBrokerName().equals(broker)).collect(Collectors.toList()));

// after
List<MessageQueue> selected = queues.stream().filter(q -> q.getBrokerName().equals(broker)).collect(Collectors.toList());
if (!selected.isEmpty()) consumer.assign(selected); else throw new IllegalStateException("no queues for broker " + broker);
Defensive patterns

Strategy: validation

Validate before calling

if (messageQueues != null && !messageQueues.isEmpty()) {
    consumer.assign(messageQueues);
} else {
    throw new IllegalStateException("queue list empty — check topic route metadata");
}

Prevention

When it happens

Trigger: consumer.assign(null) or consumer.assign(Collections.emptyList()) on a DefaultLitePullConsumer; assigning a queue list filtered down to zero entries.

Common situations: Queue lists fetched from fetchMessageQueues(topic) then filtered by tags/fields until empty; race where the topic metadata returns no queues yet and assign is called with the empty result.

Related errors


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