{"record":{"id":"54d0946837c329c2","repo":"apache/rocketmq","slug":"timeout-must-not-be-negative","errorCode":null,"errorMessage":"Timeout must not be negative","messagePattern":"Timeout must not be negative","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultLitePullConsumerImpl.java","lineNumber":610,"sourceCode":"            throw new IllegalStateException(\"setAssignTag only can be called before start.\");\n        }\n        setSubscriptionType(SubscriptionType.ASSIGN);\n        topicToSubExpression.put(topic, subExpression);\n    }\n\n    private void maybeAutoCommit() {\n        long now = System.currentTimeMillis();\n        if (now >= nextAutoCommitDeadline) {\n            commitAll();\n            nextAutoCommitDeadline = now + defaultLitePullConsumer.getAutoCommitIntervalMillis();\n        }\n    }\n\n    public synchronized List<MessageExt> poll(long timeout) {\n        try {\n            checkServiceState();\n            if (timeout < 0) {\n                throw new IllegalArgumentException(\"Timeout must not be negative\");\n            }\n\n            if (defaultLitePullConsumer.isAutoCommit()) {\n                maybeAutoCommit();\n            }\n            long endTime = System.currentTimeMillis() + timeout;\n\n            ConsumeRequest consumeRequest = consumeRequestCache.poll(endTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);\n\n            if (endTime - System.currentTimeMillis() > 0) {\n                while (consumeRequest != null && consumeRequest.getProcessQueue().isDropped()) {\n                    consumeRequest = consumeRequestCache.poll(endTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);\n                    if (endTime - System.currentTimeMillis() <= 0) {\n                        break;\n                    }\n                }\n            }\n","sourceCodeStart":592,"sourceCodeEnd":628,"githubUrl":"https://github.com/apache/rocketmq/blob/293f5885719fc4aa3619446a1900f58ccfcfdd29/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultLitePullConsumerImpl.java#L592-L628","documentation":"DefaultLitePullConsumerImpl.poll(long timeout) throws IllegalArgumentException(\"Timeout must not be negative\") when timeout < 0. poll() blocks up to timeout milliseconds waiting for a ConsumeRequest from the local cache; a negative timeout is meaningless for that wait and is rejected before any pulling occurs.","triggerScenarios":"consumer.poll(-1); passing a computed timeout that underflows (e.g. deadline - System.currentTimeMillis() after the deadline passed).","commonSituations":"Reusing Kafka poll(-1) idiom (infinite block) — RocketMQ does not support it; arithmetic on elapsed time producing negative values.","solutions":["Clamp computed timeouts: Math.max(0, remaining)","Use a large positive value (or consumerTimeoutMillisWhenSuspend tuning) for long waits instead of -1","Remember poll(0) is legal and returns immediately"],"exampleFix":"// before\nlong remain = deadline - System.currentTimeMillis();\nList<MessageExt> msgs = consumer.poll(remain);\n\n// after\nlong remain = Math.max(0, deadline - System.currentTimeMillis());\nList<MessageExt> msgs = consumer.poll(remain);","handlingStrategy":"validation","validationCode":"long safeTimeout = Math.max(0, timeout);\nList<MessageExt> msgs = consumer.poll(safeTimeout);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not reuse Kafka's poll(-1) idiom; RocketMQ requires timeout >= 0","Always clamp deadline-derived timeouts with Math.max(0, remaining)"],"tags":["rocketmq","consumer","poll","timeout","validation"],"backgroundTag":null,"analyzedSha":"293f5885719fc4aa3619446a1900f58ccfcfdd29","analyzedAt":"2026-08-14T11:50:13.822Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}