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
- Resolve a real offset first via fetchConsumeOffset / minOffset before pulling
- 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
- Do not use -1 as a pull offset sentinel — map it to minOffset or your fromWhere policy first
- Always fetch a valid committed offset before pulling
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
- maxNums <= 0
- Failed to query initial offset
- consumerGroup can not equal
- Topic can not be null or empty.
- Message queues can not be null or empty.
AI-assisted analysis of apache/rocketmq@293f588571 (2026-08-14).
Data as JSON: /api/errors/8774a2347796f204.
Report an issue: GitHub.