{"record":{"id":"0e525822eadc3532","repo":"apache/rocketmq","slug":"offset-0-0e5258","errorCode":null,"errorMessage":"offset < 0","messagePattern":"offset < 0","errorType":"exception","errorClass":"MQClientException","httpStatus":null,"severity":"error","filePath":"client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPullConsumerImpl.java","lineNumber":239,"sourceCode":"        try {\n            return FilterAPI.build(mq.getTopic(),\n                messageSelector.getExpression(), messageSelector.getExpressionType());\n        } catch (Exception e) {\n            throw new MQClientException(\"parse subscription error\", e);\n        }\n    }\n\n    private PullResult pullSyncImpl(MessageQueue mq, SubscriptionData subscriptionData, long offset, int maxNums, boolean block,\n        long timeout)\n        throws MQClientException, RemotingException, MQBrokerException, InterruptedException {\n        this.isRunning();\n\n        if (null == mq) {\n            throw new MQClientException(\"mq is null\", null);\n        }\n\n        if (offset < 0) {\n            throw new MQClientException(\"offset < 0\", null);\n        }\n\n        if (maxNums <= 0) {\n            throw new MQClientException(\"maxNums <= 0\", null);\n        }\n\n        this.subscriptionAutomatically(mq.getTopic());\n\n        int sysFlag = PullSysFlag.buildSysFlag(false, block, true, false);\n\n        long timeoutMillis = block ? this.defaultMQPullConsumer.getConsumerTimeoutMillisWhenSuspend() : timeout;\n\n        boolean isTagType = ExpressionType.isTagType(subscriptionData.getExpressionType());\n        PullResult pullResult = this.pullAPIWrapper.pullKernelImpl(\n            mq,\n            subscriptionData.getSubString(),\n            subscriptionData.getExpressionType(),\n            isTagType ? 0L : subscriptionData.getSubVersion(),","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/apache/rocketmq/blob/293f5885719fc4aa3619446a1900f58ccfcfdd29/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPullConsumerImpl.java#L221-L257","documentation":"Thrown by pullSyncImpl when the requested starting offset is negative. Pull offsets are logical positions into a commit log queue; a negative offset is meaningless and is rejected before any network call is made.","triggerScenarios":"DefaultMQPullConsumer.pull(mq, expr, offset, maxNums) with offset < 0; computing offset as 'minOffset - diff' or 'offset - pulled' that underflows; passing -1 as a sentinel 'start from beginning' value instead of fetchMessageQueuesWithQueueOffset.","commonSituations":"Developers using -1 to mean 'latest' (the correct sentinel is obtained from fetchMessageQueueOffset / maxOffset); arithmetic that subtracts more than the current offset; offsets loaded from a corrupt external store.","solutions":["Use consumer.fetchMessageQueuesWithQueueOffset(...) or the OffsetStore to obtain a valid starting offset","Clamp computed offsets: long safe = Math.max(0, offset)","For 'start at earliest/latest' semantics query minOffset/maxOffset from the broker instead of inventing sentinels"],"exampleFix":"// before\nlong offset = currentOffset - backTrack; // may go negative\nconsumer.pull(mq, \"*\", offset, 32);\n\n// after\nlong offset = Math.max(mqMinOffset, currentOffset - backTrack);\nconsumer.pull(mq, \"*\", offset, 32);","handlingStrategy":"validation","validationCode":"if (offset < 0) throw new IllegalArgumentException(\"offset must be >= 0, got \" + offset);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never use -1 as a sentinel offset","Clamp arithmetic on offsets to minOffset"],"tags":["rocketmq","pull-consumer","offset","argument-validation"],"backgroundTag":null,"analyzedSha":"293f5885719fc4aa3619446a1900f58ccfcfdd29","analyzedAt":"2026-08-14T11:50:13.822Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}