apache/pulsar · error · java.util.concurrent.CompletionException

${ex.getMessage()}

Error message

${ex.getMessage()}

What it means

The async closeAsync() maps any exception from the underlying client's close future into a CompletionException wrapping a PulsarClientException built from the original message. Callers observe it as an exceptionally-completed CompletableFuture whose cause is this exception — the graceful close of the v4 client failed.

Source

Thrown at pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/PulsarClientV5.java:113

        if (transactionTimeout != null) {
            builder.withTransactionTimeout(transactionTimeout.toMillis(), TimeUnit.MILLISECONDS);
        }
        return builder.build().thenApply(v4Txn -> (Transaction) new TransactionV5(v4Txn));
    }

    @Override
    public void close() throws PulsarClientException {
        try {
            v4Client.close();
        } catch (org.apache.pulsar.client.api.PulsarClientException e) {
            throw new PulsarClientException(e.getMessage(), e);
        }
    }

    @Override
    public CompletableFuture<Void> closeAsync() {
        return v4Client.closeAsync().exceptionally(ex -> {
            throw new CompletionException(new PulsarClientException(ex.getMessage(), ex));
        });
    }

    @Override
    public void shutdown() {
        try {
            v4Client.shutdown();
        } catch (org.apache.pulsar.client.api.PulsarClientException e) {
            throw new RuntimeException(e);
        }
    }
}

View on GitHub (pinned to 820761864e)

Solutions

  1. Handle the exceptionally-completed future: inspect the wrapped PulsarClientException cause for the root failure
  2. Retry closeAsync if the failure was transient (network), or fall back to shutdown() for forceful teardown
  3. Ensure you are not closing the same client twice concurrently

Example fix

// before
client.closeAsync(); // exception ignored or surfaces far away
// after
client.closeAsync().exceptionally(ex -> {
    log.warn("Client close failed", ex);
    return null;
});
Defensive patterns

Strategy: try-catch

Try / catch

client.closeAsync().whenComplete((v, ex) -> {
    if (ex != null) log.warn("Async close failed: {}", ex.getMessage(), ex.getCause());
});

Prevention

When it happens

Trigger: CompletableFuture returned by PulsarClientV5.closeAsync() completes exceptionally when the wrapped client's closeAsync fails: broker unreachable during producer/consumer teardown, or the client is already closed.

Common situations: Closing clients during a network outage; shutdown sequences where closeAsync is chained and the wrapped cause surfaces in logs; tests tearing down clients after brokers were stopped.

Related errors


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