apache/pulsar · error · org.apache.pulsar.client.impl.v5.PulsarClientException

${e.getCause()}

Error message

${e.getCause()}

What it means

The synchronous subscribe() joins subscribeAsync(); a CompletionException from the join is unwrapped — if the cause is a PulsarClientException it is re-thrown as-is, otherwise it is wrapped in a new PulsarClientException whose message is the cause's toString (${e.getCause()}). It indicates the subscription of the queue consumer failed for a reason outside the normal PulsarClientException set (e.g. schema/serializer failure, runtime error in builder internals).

Source

Thrown at pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/QueueConsumerBuilderV5.java:72

     * one-per-segment design, which would also reject {@code topic://} scalable DLQ
     * targets).
     */
    private DeadLetterPolicy dlqPolicy;

    QueueConsumerBuilderV5(PulsarClientV5 client, Schema<T> v5Schema) {
        this.client = client;
        this.v5Schema = v5Schema;
    }

    @Override
    public QueueConsumer<T> subscribe() throws PulsarClientException {
        try {
            return subscribeAsync().join();
        } catch (java.util.concurrent.CompletionException e) {
            if (e.getCause() instanceof PulsarClientException pce) {
                throw pce;
            }
            throw new PulsarClientException(e.getCause());
        }
    }

    @Override
    public CompletableFuture<QueueConsumer<T>> subscribeAsync() {
        boolean topicSet = topicName != null && !topicName.isEmpty();
        boolean namespaceSet = namespaceName != null;
        if (topicSet == namespaceSet) {
            return CompletableFuture.failedFuture(
                    new PulsarClientException.InvalidConfigurationException(
                            "Exactly one of .topic(name) or .namespace(...) must be set"));
        }
        if (conf.getSubscriptionName() == null || conf.getSubscriptionName().isEmpty()) {
            return CompletableFuture.failedFuture(
                    new PulsarClientException.InvalidConfigurationException("Subscription name is required"));
        }

        if (namespaceSet) {

View on GitHub (pinned to 820761864e)

Solutions

  1. Read the wrapped cause (printed in the message) to identify the non-standard failure
  2. Verify the builder state: topicName set, subscriptionName set, schema provided and matching the topic
  3. Fix the root cause and retry subscribe(); route broker-level failures to standard PulsarClientException handling

Example fix

// before
QueueConsumer<String> c = client.newConsumer(Schema.STRING).subscribe(); // topic never set
// after
QueueConsumer<String> c = client.newConsumer(Schema.STRING)
    .topic("persistent://public/default/my-topic")
    .subscriptionName("my-sub")
    .subscribe();
Defensive patterns

Strategy: validation

Validate before calling

if (topicName == null || topicName.isBlank()) throw new IllegalStateException("topic must be set before subscribe");
if (subscriptionName == null || subscriptionName.isBlank()) throw new IllegalStateException("subscriptionName must be set before subscribe");

Try / catch

try {
    QueueConsumer<T> c = builder.subscribe();
} catch (PulsarClientException e) {
    log.error("Subscribe failed: {}", e.getMessage(), e.getCause());
}

Prevention

When it happens

Trigger: Calling QueueConsumerBuilderV5.subscribe() when the async subscription completes exceptionally: invalid combination of builder options (topic/subscription validation failure throwing a non-PulsarClientException like IllegalStateException), schema errors, or broker connection failures that arrive wrapped.

Common situations: Forgetting to set a topic or subscription name before subscribe(); using a schema incompatible with existing topic data; misconfigured dead-letter/retry options; broker down or authorization rejected.

Related errors


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