apache/rocketmq · error · MQClientException

pullCallback is null

Error message

pullCallback is null

What it means

Thrown by pullAsyncImpl when the PullCallback argument is null. Because the async API delivers its result exclusively through the callback, a null callback would make the result unreachable and lose pull outcomes; the client rejects it up front.

Source

Thrown at client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPullConsumerImpl.java:523

        if (null == mq) {
            throw new MQClientException("mq is null", null);
        }

        if (offset < 0) {
            throw new MQClientException("offset < 0", null);
        }

        if (maxNums <= 0) {
            throw new MQClientException("maxNums <= 0", null);
        }

        if (maxSizeInBytes <= 0) {
            throw new MQClientException("maxSizeInBytes <= 0", null);
        }


        if (null == pullCallback) {
            throw new MQClientException("pullCallback is null", null);
        }

        this.subscriptionAutomatically(mq.getTopic());

        try {
            int sysFlag = PullSysFlag.buildSysFlag(false, block, true, false);

            long timeoutMillis = block ? this.defaultMQPullConsumer.getConsumerTimeoutMillisWhenSuspend() : timeout;

            boolean isTagType = ExpressionType.isTagType(subscriptionData.getExpressionType());
            this.pullAPIWrapper.pullKernelImpl(
                mq,
                subscriptionData.getSubString(),
                subscriptionData.getExpressionType(),
                isTagType ? 0L : subscriptionData.getSubVersion(),
                offset,
                maxNums,
                maxSizeInBytes,

View on GitHub (pinned to 293f588571)

Solutions

  1. Always supply a non-null PullCallback; implement both onSuccess and onException
  2. Use the synchronous pull() if you want a return value instead of a callback
  3. Initialize callback fields before starting any thread or timer that issues pulls

Example fix

// before
consumer.pullAsync(mq, "*", off, 32, null);

// after
consumer.pullAsync(mq, "*", off, 32, new PullCallback() {
    public void onSuccess(PullResult result) { /* handle */ }
    public void onException(Throwable e) { /* log + retry */ }
});
Defensive patterns

Strategy: validation

Validate before calling

Objects.requireNonNull(pullCallback, "pullCallback");

Prevention

When it happens

Trigger: DefaultMQPullConsumer.pullAsync(mq, expr, offset, maxNums, null); a callback field not yet initialized when the pull loop starts; refactoring inline anonymous PullCallback classes into fields and forgetting the assignment.

Common situations: Constructor-order bugs where pulls are scheduled before the callback is wired; test harnesses passing null intending to use a returned future (which the async API does not provide).

Related errors


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