apache/pulsar · error · IllegalArgumentException

Not supported operation on partitioned-topic

Error message

Not supported operation on partitioned-topic

What it means

ResetCursorData's constructor extracts ledgerId, entryId, batchIndex, and partitionIndex from a MessageId by casting it to MessageIdAdv. If the caller passes a TopicMessageId — the aggregated message id type used when consuming a partitioned topic — the code rejects it because a TopicMessageId does not carry a single partition's raw position suitable for cursor reset.

Source

Thrown at pulsar-client/src/main/java/org/apache/pulsar/client/impl/ResetCursorData.java:78

            this.entryId = Long.MAX_VALUE;
        } else if ("earliest".equals(position)) {
            this.ledgerId = -1;
            this.entryId = -1;
        } else {
            throw new IllegalArgumentException(
                    String.format("Invalid value %s for the position. Allowed values are [latest, earliest]",
                            position));
        }
    }

    public ResetCursorData(MessageId messageId) {
        MessageIdAdv messageIdAdv = (MessageIdAdv) messageId;
        this.ledgerId = messageIdAdv.getLedgerId();
        this.entryId = messageIdAdv.getEntryId();
        this.batchIndex = messageIdAdv.getBatchIndex();
        this.partitionIndex = messageIdAdv.getPartitionIndex();
        if (messageId instanceof TopicMessageId) {
            throw new IllegalArgumentException("Not supported operation on partitioned-topic");
        }
    }

}

View on GitHub (pinned to 820761864e)

Solutions

  1. Unwrap the TopicMessageId to its underlying partition message id (e.g. topicMessageId.getInnerMessageId() or cast the TopicMessageId and get the partition-specific MessageId) before constructing ResetCursorData.
  2. Obtain the message id directly from a consumer attached to the specific partition (partition-i topic) rather than from the aggregated partitioned-topic consumer.
  3. If the topic is partitioned and you intend to reset the whole topic, use the partition-aware admin API instead of ResetCursorData with a single message id.

Example fix

// before
MessageId id = message.getMessageId(); // TopicMessageId on partitioned topic
ResetCursorData data = new ResetCursorData(id);

// after
MessageId id = message.getMessageId();
if (id instanceof TopicMessageId) {
    id = ((TopicMessageId) id).getInnerMessageId();
}
ResetCursorData data = new ResetCursorData(id);
Defensive patterns

Strategy: type-guard

Validate before calling

if (messageId instanceof TopicMessageId) {
    throw new IllegalArgumentException("Pass the partition-level message id, not a TopicMessageId");
}

Type guard

static MessageId requirePartitionMessageId(MessageId id) {
    return id instanceof TopicMessageId ? ((TopicMessageId) id).getInnerMessageId() : id;
}

Prevention

When it happens

Trigger: Calling the ResetCursorData constructor (directly or via an admin/client API that builds one) with a MessageId obtained from a consumer subscribed to a partitioned topic, i.e. an instance of TopicMessageId, instead of the underlying partition-level message id.

Common situations: Consuming a partitioned topic and passing message.getMessageId() straight into a reset-cursor / seek style call without unwrapping it to the partition's MessageIdAdv; code written against a non-partitioned topic then reused against a partitioned topic.

Related errors


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