apache/rocketmq · error · MQClientException

offset < 0

Error message

offset < 0

What it means

pullSyncImpl throws MQClientException("offset < 0") when the requested starting offset is negative. Pull always begins at an absolute committed offset; negative values are not valid positions (there is no 'newest'/'oldest' sentinel offset in this API — those are chosen via fromWhere at assignment time).

Source

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

        return pull(mq, subscriptionData, offset, maxNums, this.defaultLitePullConsumer.getConsumerPullTimeoutMillis());
    }

    private PullResult pull(MessageQueue mq, SubscriptionData subscriptionData, long offset, int maxNums, long timeout)
        throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
        return this.pullSyncImpl(mq, subscriptionData, offset, maxNums, true, timeout);
    }

    private PullResult pullSyncImpl(MessageQueue mq, SubscriptionData subscriptionData, long offset, int maxNums,
        boolean block,
        long timeout)
        throws MQClientException, RemotingException, MQBrokerException, InterruptedException {

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

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

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

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

View on GitHub (pinned to 293f588571)

Solutions

  1. Resolve a real offset first via fetchConsumeOffset / minOffset before pulling
  2. Map your own 'no offset' sentinel (-1) to consumer.minOffset(q) or the configured fromWhere policy

Example fix

// before
long off = myStore.get(q); // -1 when absent
consumer.pull(q, "*", off, 32, 3000);

// after
long off = myStore.containsKey(q) ? myStore.get(q) : consumer.minOffset(q);
consumer.pull(q, "*", Math.max(0, off), 32, 3000);
Defensive patterns

Strategy: validation

Validate before calling

long safeOffset = Math.max(0, offset);
// or resolve a real start offset first:
if (offset < 0) offset = consumer.minOffset(q);

Prevention

When it happens

Trigger: consumer.pull(q, expr, -1, maxNums, timeout); passing a computed offset that is -1 as a 'not found' sentinel from your own offset store.

Common situations: Your own offset store returning -1 as a 'no offset yet' sentinel and feeding it straight to pull; reusing Kafka's offset semantics where -1 means latest.

Related errors


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