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).
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
- Another partition exists for
- AutoConsumeSchema is not intended to be used for encoding
- AutoConsumeSchema is not supported with schemaId
- Bookie failed to start within timeout period
- Bookie 'group' parameters is missing
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/c114aff9a28e6b88.
Report an issue: GitHub.
Appendix: 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)