{"record":{"id":"a9ccd56825ed1441","repo":"apache/pulsar","slug":"isduplicated-cannot-accept","errorCode":null,"errorMessage":"isDuplicated cannot accept ","messagePattern":"isDuplicated cannot accept ","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"warning","filePath":"pulsar-client/src/main/java/org/apache/pulsar/client/impl/PersistentAcknowledgmentsGroupingTracker.java","lineNumber":121,"sourceCode":"        this.currentCumulativeAckFuture = new TimedCompletableFuture<>();\n\n        if (acknowledgementGroupTimeMicros > 0) {\n            scheduledTask = eventLoopGroup.next().scheduleWithFixedDelay(catchingAndLoggingThrowables(this::flush),\n                    acknowledgementGroupTimeMicros,\n                    acknowledgementGroupTimeMicros, TimeUnit.MICROSECONDS);\n        } else {\n            scheduledTask = null;\n        }\n    }\n\n    /**\n     * Since the ack are delayed, we need to do some best-effort duplicate check to discard messages that are being\n     * resent after a disconnection and for which the user has already sent an acknowledgement.\n     */\n    @Override\n    public boolean isDuplicate(MessageId messageId) {\n        if (!(messageId instanceof MessageIdAdv)) {\n            throw new IllegalArgumentException(\"isDuplicated cannot accept \"\n                    + messageId.getClass().getName() + \": \" + messageId);\n        }\n        final MessageIdAdv messageIdAdv = (MessageIdAdv) messageId;\n        if (lastCumulativeAck.compareTo(messageIdAdv) >= 0) {\n            // Already included in a cumulative ack\n            return true;\n        } else {\n            // If \"batchIndexAckEnabled\" is false, the batched messages acknowledgment will be traced by\n            // pendingIndividualAcks. So no matter what type the message ID is, check with \"pendingIndividualAcks\"\n            // first.\n            MessageIdAdv key = MessageIdAdvUtils.discardBatch(messageIdAdv);\n            if (pendingIndividualAcks.contains(key)) {\n                return true;\n            }\n            if (messageIdAdv.getBatchIndex() >= 0) {\n                ConcurrentBitSet bitSet = pendingIndividualBatchIndexAcks.get(key);\n                return bitSet != null && !bitSet.get(messageIdAdv.getBatchIndex());\n            }","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-client/src/main/java/org/apache/pulsar/client/impl/PersistentAcknowledgmentsGroupingTracker.java#L103-L139","documentation":"PersistentAcknowledgmentsGroupingTracker.isDuplicate() only accepts MessageIdAdv instances (the advanced id interface exposing bit-set/ack-state fields). Passing a plain MessageId (e.g. a deserialized or non-adv id) throws IllegalArgumentException. The tracker relies on MessageIdAdv internals for its best-effort duplicate detection of already-acked messages being resent after reconnection.","triggerScenarios":"Calling isDuplicate() with a MessageId that is not MessageIdAdv — commonly an id obtained via MessageId.fromByteArray/fromByteArrayWithTopic, a copied id, or a custom id implementation.","commonSituations":"Deserializing message ids from checkpoints and feeding them back into ack/duplicate checks; wrapping or transforming ids in connector code; version mismatches where an older client's id class lacks the MessageIdAdv interface.","solutions":["Pass the original MessageId received from consumer.receive() (always MessageIdAdv in current clients) to isDuplicate()","If starting from serialized bytes, use the consumer/broker round-trip rather than a bare MessageId.fromByteArray result, or reconstruct via the tracking layer that yields adv ids","Guard with instanceof MessageIdAdv and skip the duplicate check for non-adv ids","Catch IllegalArgumentException and treat the message as not-duplicate (safe default: let normal ack flow proceed)"],"exampleFix":"// before\nboolean dup = tracker.isDuplicate(MessageId.fromByteArray(bytes)); // IllegalArgumentException\n// after\nMessageId id = MessageId.fromByteArray(bytes);\nif (id instanceof MessageIdAdv) {\n    boolean dup = tracker.isDuplicate(id);\n} else {\n    // non-adv id: skip best-effort duplicate detection\n    boolean dup = false;\n}","handlingStrategy":"type-guard","validationCode":"boolean duplicateCheckable(MessageId id) {\n    return id instanceof MessageIdAdv;\n}","typeGuard":"boolean isAdv(MessageId id) {\n    return id instanceof MessageIdAdv;\n}","tryCatchPattern":"try {\n    boolean dup = tracker.isDuplicate(id);\n} catch (IllegalArgumentException e) {\n    // non-adv id: treat as not duplicate and proceed with normal ack flow\n}","preventionTips":["Only pass MessageIds received directly from consumer.receive() into ack/duplicate APIs","Avoid round-tripping ids through fromByteArray without preserving the adv implementation","Don't wrap MessageId in custom subclasses that drop the MessageIdAdv interface"],"tags":["pulsar","acknowledgment","messageid","illegal-argument","type-guard"],"backgroundTag":"messageid-type-unsupported","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}