apache/pulsar · warning · java.lang.UnsupportedOperationException
Expire message by position is not supported for non-persiste
Error message
Expire message by position is not supported for non-persistent topic.
What it means
NonPersistentSubscription.expireMessages(Position position) unconditionally throws UnsupportedOperationException — expiring messages up to a specific position (cumulative expiry) is not meaningful for non-persistent topics since messages are not retained in storage.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/service/nonpersistent/NonPersistentSubscription.java:510
return Collections.emptyList();
}
}
@Override
public boolean expireMessages(int messageTTLInSeconds) {
throw new UnsupportedOperationException("Expire message by timestamp is not supported for"
+ " non-persistent topic.");
}
@Override
public CompletableFuture<Boolean> expireMessagesAsync(int messageTTLInSeconds) {
return CompletableFuture.failedFuture(new UnsupportedOperationException("Expire message by timestamp is not"
+ " supported for non-persistent topic."));
}
@Override
public boolean expireMessages(Position position) {
throw new UnsupportedOperationException("Expire message by position is not supported for"
+ " non-persistent topic.");
}
public NonPersistentSubscriptionStatsImpl getStats(GetStatsOptions getStatsOptions) {
NonPersistentSubscriptionStatsImpl subStats = new NonPersistentSubscriptionStatsImpl();
subStats.bytesOutCounter = bytesOutFromRemovedConsumers.longValue();
subStats.msgOutCounter = msgOutFromRemovedConsumer.longValue();
NonPersistentDispatcher dispatcher = this.dispatcher;
if (dispatcher != null) {
dispatcher.getConsumers().forEach(consumer -> {
ConsumerStatsImpl consumerStats = consumer.getStats();
if (!getStatsOptions.isExcludeConsumers()) {
subStats.consumers.add(consumerStats);
}
subStats.msgRateOut += consumerStats.msgRateOut;
subStats.messageAckRate += consumerStats.messageAckRate;
subStats.msgThroughputOut += consumerStats.msgThroughputOut;View on GitHub (pinned to 820761864e)
Solutions
- Restrict position-based expiry calls to persistent topics
- Check topic type via admin API (persistent topic namespace) before issuing expire-messages
- Handle the UnsupportedOperationException (or its async failed-future equivalent) in admin tooling
- Use disconnect-consumer or clear-backlog operations instead for non-persistent subscriptions
Example fix
// before
admin.topics().expireMessages(topic, subName, position);
// after
if (topic.startsWith("persistent://")) {
admin.topics().expireMessages(topic, subName, position);
} Defensive patterns
Strategy: type-guard
Validate before calling
if (!topicName.getPersistent()) {
return; // skip position-based expiry
}
admin.topics().expireMessages(topic, subName, position); Type guard
boolean supportsPositionExpiry(String topic) {
return topic.startsWith("persistent://");
} Try / catch
try {
subscription.expireMessages(position);
} catch (UnsupportedOperationException e) {
log.debug("Position-based expiry unsupported for non-persistent topic");
} Prevention
- Gate position-based expiry tooling on persistent:// scheme
- Train ops on non-persistent topic limitations (no backlog/retention semantics)
- Use clear-backlog or disconnect instead for non-persistent subscriptions
When it happens
Trigger: Calling subscription.expireMessages(Position) or admin REST/CLI 'expire messages up to position' against a subscription on a non-persistent topic.
Common situations: Admin console/API expire-messages-by-position invoked on non-persistent topic; automation scripts applying position-based expiry uniformly across topics.
Related errors
- Expire message by timestamp is not supported for non-persist
- PublishTxnMessage is not supported by non-persistent topic
- Failed to add schema to an active topic with empty(BYTES) sc
- Package Management Service is not enabled in the broker.
- ${key} already exists in the dynamicConfigurationMap
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/8cd58cd134710922.
Report an issue: GitHub.