apache/pulsar · error · IllegalArgumentException

Expected MessageIdV5, got: + messageId.getClass()

Error message

Expected MessageIdV5, got: + messageId.getClass()

What it means

Transactional cumulative ack on ScalableStreamConsumer requires MessageIdV5 because its positionVector drives per-segment acks inside the (unwrapped) v4 transaction. Other MessageId implementations have no vector, so IllegalArgumentException is thrown synchronously and the transaction is unaffected.

Source

Thrown at pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/ScalableStreamConsumer.java:304

        return new MessagesV5<>(receiveQueue.receiveMulti(maxNumMessages, timeout));
    }

    @Override
    public void acknowledgeCumulative(MessageId messageId) {
        if (!(messageId instanceof MessageIdV5 id)) {
            throw new IllegalArgumentException("Expected MessageIdV5, got: " + messageId.getClass());
        }

        // Ack each segment up to the position recorded in the vector
        for (var entry : id.positionVector().entrySet()) {
            ackSegmentUpTo(entry.getKey(), entry.getValue(), null);
        }
    }

    @Override
    public void acknowledgeCumulative(MessageId messageId, Transaction txn) {
        if (!(messageId instanceof MessageIdV5 id)) {
            throw new IllegalArgumentException("Expected MessageIdV5, got: " + messageId.getClass());
        }
        var v4Txn = TransactionV5.unwrap(txn);
        for (var entry : id.positionVector().entrySet()) {
            ackSegmentUpTo(entry.getKey(), entry.getValue(), v4Txn);
        }
    }

    /**
     * Ack one segment up to the given position. Whole-segment (Exclusive) consumers ack
     * cumulatively. PIP-486 bucket-shared segments are Key_Shared underneath, where cumulative acks
     * are not permitted — the ack is translated into individually acking every delivered-but-unacked
     * id up to the position (exactly the messages this consumer received: its buckets' share).
     */
    private void ackSegmentUpTo(long segmentId, org.apache.pulsar.client.api.MessageId position,
                                org.apache.pulsar.client.api.transaction.Transaction v4Txn) {
        // A draining consumer takes precedence: during a release the segment's slot in
        // segmentConsumers is vacated (or already holds the re-subscribe chain), but the acks that
        // complete the drain must still reach the consumer being drained.

View on GitHub (pinned to 820761864e)

Solutions

  1. Use MessageIdV5 ids exclusively inside transactions on this consumer
  2. Convert persisted ids to MessageIdV5 (reconstructing positionVector) before acking
  3. Guard transactional ack code with instanceof MessageIdV5 checks
  4. Keep v4 and v5 transactional consumers on separate code paths

Example fix

// before
streamConsumer.acknowledgeCumulative(v4Id, txn);
// after
if (v4Id instanceof MessageIdV5 id) {
    streamConsumer.acknowledgeCumulative(id, txn);
} else {
    txn.abort();
    throw new IllegalArgumentException("need MessageIdV5 with positionVector");
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(messageId instanceof MessageIdV5 id) || id.positionVector() == null) {
    throw new IllegalArgumentException("transactional cumulative ack requires MessageIdV5");
}

Type guard

static boolean isCumulativeCapable(MessageId id) {
    return id instanceof MessageIdV5 v && v.positionVector() != null;
}

Try / catch

try {
    streamConsumer.acknowledgeCumulative(messageId, txn);
} catch (IllegalArgumentException e) {
    txn.abort();
    log.error("transactional cumulative ack type error", e);
}

Prevention

When it happens

Trigger: acknowledgeCumulative(messageId, txn) called with a non-v5 id: an id from a v4 consumer, a deserialized v4 id, or an id produced by a different stream consumer, while a transaction is open.

Common situations: Transaction spanning messages from v4 and v5 consumers; replaying recorded ids from an old storage format inside a new transaction; framework code typed on the generic MessageId API.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/aeed9f83b02c1a61. Report an issue: GitHub.