apache/rocketmq · error · IllegalArgumentException

subExpression can not be null or empty.

Error message

subExpression can not be null or empty.

What it means

DefaultLitePullConsumerImpl.setSubExpressionForAssign(topic, subExpression) throws this IllegalArgumentException when subExpression is null, empty, or whitespace (StringUtils.isBlank). The API replaces the default SUB_ALL tag filter for an assigned queue, so a blank expression has no meaning and is rejected.

Source

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

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

    private void maybeAutoCommit() {
        long now = System.currentTimeMillis();
        if (now >= nextAutoCommitDeadline) {
            commitAll();
            nextAutoCommitDeadline = now + defaultLitePullConsumer.getAutoCommitIntervalMillis();
        }
    }

    public synchronized List<MessageExt> poll(long timeout) {
        try {

View on GitHub (pinned to 293f588571)

Solutions

  1. Default the expression to "*" (SUB_ALL) when blank before calling the method
  2. Validate the expression against the legal TAG syntax if it comes from external input

Example fix

// before
consumer.setSubExpressionForAssign(topic, tagFromConfig);

// after
String expr = (tagFromConfig == null || tagFromConfig.trim().isEmpty()) ? "*" : tagFromConfig;
consumer.setSubExpressionForAssign(topic, expr);
Defensive patterns

Strategy: validation

Validate before calling

String expr = (subExpression == null || subExpression.trim().isEmpty()) ? "*" : subExpression;
consumer.setSubExpressionForAssign(topic, expr);

Prevention

When it happens

Trigger: Calling setSubExpressionForAssign(topic, ""), (topic, null), or (topic, " ") after using assign().

Common situations: Tag expression sourced from user input or config that is blank; passing a trimmed-out placeholder value.

Related errors


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