apache/pulsar · warning · java.lang.UnsupportedOperationException

PublishTxnMessage is not supported by non-persistent topic

Error message

PublishTxnMessage is not supported by non-persistent topic

What it means

NonPersistentTopic.publishTxnMessage unconditionally throws UnsupportedOperationException: transactional message publishing requires durable state (pending acks, txn markers) that non-persistent topics cannot maintain, so transactions are unsupported on them.

Source

Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/service/nonpersistent/NonPersistentTopic.java:1330

                return checkSchemaCompatibleForConsumer(schema)
                        .exceptionally(ex -> {
                            Throwable realCause = FutureUtil.unwrapCompletionException(ex);
                            if (realCause instanceof NotExistSchemaException) {
                                throw FutureUtil.wrapToCompletionException(
                                        new IncompatibleSchemaException("Failed to add schema to an active topic"
                                                + " with empty(BYTES) schema: new schema type " + schema.getType()));
                            }
                            throw FutureUtil.wrapToCompletionException(realCause);
                        });
            } else {
                return addSchema(schema).thenCompose(schemaVersion -> CompletableFuture.completedFuture(null));
            }
        });
    }

    @Override
    public void publishTxnMessage(TxnID txnID, ByteBuf headersAndPayload, PublishContext publishContext) {
        throw new UnsupportedOperationException("PublishTxnMessage is not supported by non-persistent topic");
    }

    @Override
    public CompletableFuture<Void> endTxn(TxnID txnID, int txnAction, long lowWaterMark) {
        return FutureUtil.failedFuture(
                new Exception("Unsupported operation endTxn in non-persistent topic."));
    }

    @Override
    public CompletableFuture<Void> truncate() {
        return FutureUtil.failedFuture(new NotAllowedException("Unsupported truncate"));
    }

    protected boolean isTerminated() {
        return false;
    }

View on GitHub (pinned to 820761864e)

Solutions

  1. Use persistent topics for transactional producers
  2. Route transactional sends to persistent topics only; check topic type before beginTransaction/send
  3. Disable transactions for producers bound to non-persistent topics
  4. Catch UnsupportedOperationException and fall back to non-transactional publish

Example fix

// before
Transaction txn = client.newTransaction().build();
nonPersistentProducer.newMessage(txn).send();
// after
Producer persistentProducer = client.newProducer()
    .topic("persistent://public/default/txn-topic").create();
persistentProducer.newMessage(txn).send();
Defensive patterns

Strategy: type-guard

Validate before calling

if (!topicName.getPersistent()) {
    // route through a non-transactional producer instead
    return producer.newMessage(value).send();
}

Type guard

boolean supportsTxnPublish(String topic) {
    return topic.startsWith("persistent://");
}

Try / catch

try {
    producer.newMessage(txn).send();
} catch (UnsupportedOperationException e) {
    log.warn("Txn publish unsupported on non-persistent topic; sending non-txn");
}

Prevention

When it happens

Trigger: Producing a transactional message (transaction.newBuilder().send(...) via producer) to a non-persistent topic; endTxn on a non-persistent topic likewise fails with a failed future.

Common situations: Application configured with transactions enabled but producers targeting non-persistent topics; framework/messaging abstraction picking non-persistent topic while using Pulsar transactions; tests mixing transaction features with non-persistent topics.

Related errors


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