apache/pulsar · error · UnsupportedOperationException
BookKeeper client is not available
Error message
BookKeeper client is not available
What it means
getBookKeeperClient() throws UnsupportedOperationException when the broker's managed-ledger storage default class is not the BookKeeper-based implementation. The broker can be configured with alternative storage backends, and a raw BookKeeper client only exists for the default BookKeeper storage class, so the method refuses to return anything for other backends (see the TODO in the source).
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java:1662
return this.nsService;
}
public Optional<WorkerService> getWorkerServiceOpt() {
return functionWorkerService;
}
public WorkerService getWorkerService() throws UnsupportedOperationException {
return functionWorkerService.orElseThrow(() -> new UnsupportedOperationException("Pulsar Function Worker "
+ "is not enabled, probably functionsWorkerEnabled is set to false"));
}
public BookKeeper getBookKeeperClient() {
ManagedLedgerStorageClass defaultStorageClass = getManagedLedgerStorage().getDefaultStorageClass();
if (defaultStorageClass instanceof BookkeeperManagedLedgerStorageClass bkStorageClass) {
return bkStorageClass.getBookKeeperClient();
} else {
// TODO: Refactor code to support other than default bookkeeper based storage class
throw new UnsupportedOperationException("BookKeeper client is not available");
}
}
public ManagedLedgerFactory getDefaultManagedLedgerFactory() {
return getManagedLedgerStorage().getDefaultStorageClass().getManagedLedgerFactory();
}
/**
* First, get <code>LedgerOffloader</code> from local map cache,
* create new <code>LedgerOffloader</code> if not in cache or
* the <code>OffloadPolicies</code> changed, return the <code>LedgerOffloader</code> directly if exist in cache
* and the <code>OffloadPolicies</code> not changed.
*
* @param namespaceName NamespaceName
* @param offloadPolicies the OffloadPolicies
* @return LedgerOffloader
*/
public LedgerOffloader getManagedLedgerOffloader(NamespaceName namespaceName, OffloadPoliciesImpl offloadPolicies) {View on GitHub (pinned to 820761864e)
Solutions
- Ensure the broker uses the default BookKeeper-based managed ledger storage so getBookKeeperClient() returns a client.
- Guard your code: check getManagedLedgerStorage().getDefaultStorageClass() instanceof BookkeeperManagedLedgerStorageClass before calling the getter, and handle the alternative case.
- If you maintain the extension, track the upstream TODO to refactor the API for non-default storage classes rather than catching and working around it.
- Access BookKeeper indirectly via ledger APIs (e.g. through the managed ledger factory) where the backend type does not matter.
Example fix
// before
BookKeeper bk = pulsar.getBookKeeperClient();
// after
if (pulsar.getManagedLedgerStorage().getDefaultStorageClass() instanceof BookkeeperManagedLedgerStorageClass bkStorage) {
BookKeeper bk = bkStorage.getBookKeeperClient();
} else {
// fall back to backend-agnostic managed ledger APIs
} Defensive patterns
Strategy: type-guard
Validate before calling
boolean bkAvailable = pulsar.getManagedLedgerStorage().getDefaultStorageClass()
instanceof BookkeeperManagedLedgerStorageClass;
if (!bkAvailable) {
// skip BookKeeper-specific code paths
} Type guard
boolean isBookKeeperStorage(PulsarService pulsar) {
return pulsar.getManagedLedgerStorage().getDefaultStorageClass()
instanceof BookkeeperManagedLedgerStorageClass;
} Try / catch
try {
BookKeeper bk = pulsar.getBookKeeperClient();
// use bk
} catch (UnsupportedOperationException e) {
if ("BookKeeper client is not available".equals(e.getMessage())) {
// non-BookKeeper storage backend: use managed-ledger APIs instead
} else {
throw e;
}
} Prevention
- Never assume the default BookKeeper storage class; guard with instanceof before calling getBookKeeperClient().
- Prefer managed-ledger or ledger-agnostic APIs in protocol handlers and plugins.
- Check the broker's storage configuration in tests that exercise this getter.
- Track upstream changes to this TODO'd API so your extension adapts when non-default storage classes are supported.
When it happens
Trigger: Calling pulsar.getBookKeeperClient() (commonly from protocol handlers, plugins, extensions, or tests) on a broker whose managed ledger storage default storage class is not BookkeeperManagedLedgerStorageClass.
Common situations: Custom/experimental storage backends or test harnesses that replace managed ledger storage; plugin code assuming the default BookKeeper backend; brokers running with alternative storage configured via configuration.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Cursor %s mark-delete position %s is ahead of the last posit
- Timeout during managed ledger close
- Timeout during managed ledger delete operation
- Timeout during managed ledger offload operation
- rereplicationEntryBatchSize should be smaller than maxPendin
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/c114aff9a28e6b88.
Report an issue: GitHub.