{"record":{"id":"c114aff9a28e6b88","repo":"apache/pulsar","slug":"bookkeeper-client-is-not-available","errorCode":null,"errorMessage":"BookKeeper client is not available","messagePattern":"BookKeeper client is not available","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java","lineNumber":1662,"sourceCode":"        return this.nsService;\n    }\n\n    public Optional<WorkerService> getWorkerServiceOpt() {\n        return functionWorkerService;\n    }\n\n    public WorkerService getWorkerService() throws UnsupportedOperationException {\n        return functionWorkerService.orElseThrow(() -> new UnsupportedOperationException(\"Pulsar Function Worker \"\n                + \"is not enabled, probably functionsWorkerEnabled is set to false\"));\n    }\n\n    public BookKeeper getBookKeeperClient() {\n        ManagedLedgerStorageClass defaultStorageClass = getManagedLedgerStorage().getDefaultStorageClass();\n        if (defaultStorageClass instanceof BookkeeperManagedLedgerStorageClass bkStorageClass) {\n            return bkStorageClass.getBookKeeperClient();\n        } else {\n            // TODO: Refactor code to support other than default bookkeeper based storage class\n            throw new UnsupportedOperationException(\"BookKeeper client is not available\");\n        }\n    }\n\n    public ManagedLedgerFactory getDefaultManagedLedgerFactory() {\n        return getManagedLedgerStorage().getDefaultStorageClass().getManagedLedgerFactory();\n    }\n\n    /**\n     * First, get <code>LedgerOffloader</code> from local map cache,\n     * create new <code>LedgerOffloader</code> if not in cache or\n     * the <code>OffloadPolicies</code> changed, return the <code>LedgerOffloader</code> directly if exist in cache\n     * and the <code>OffloadPolicies</code> not changed.\n     *\n     * @param namespaceName NamespaceName\n     * @param offloadPolicies the OffloadPolicies\n     * @return LedgerOffloader\n     */\n    public LedgerOffloader getManagedLedgerOffloader(NamespaceName namespaceName, OffloadPoliciesImpl offloadPolicies) {","sourceCodeStart":1644,"sourceCodeEnd":1680,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java#L1644-L1680","documentation":"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).","triggerScenarios":"Calling pulsar.getBookKeeperClient() (commonly from protocol handlers, plugins, extensions, or tests) on a broker whose managed ledger storage default storage class is not BookkeeperManagedLedgerStorageClass.","commonSituations":"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.","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."],"exampleFix":"// before\nBookKeeper bk = pulsar.getBookKeeperClient();\n// after\nif (pulsar.getManagedLedgerStorage().getDefaultStorageClass() instanceof BookkeeperManagedLedgerStorageClass bkStorage) {\n    BookKeeper bk = bkStorage.getBookKeeperClient();\n} else {\n    // fall back to backend-agnostic managed ledger APIs\n}","handlingStrategy":"type-guard","validationCode":"boolean bkAvailable = pulsar.getManagedLedgerStorage().getDefaultStorageClass()\n        instanceof BookkeeperManagedLedgerStorageClass;\nif (!bkAvailable) {\n    // skip BookKeeper-specific code paths\n}","typeGuard":"boolean isBookKeeperStorage(PulsarService pulsar) {\n    return pulsar.getManagedLedgerStorage().getDefaultStorageClass()\n        instanceof BookkeeperManagedLedgerStorageClass;\n}","tryCatchPattern":"try {\n    BookKeeper bk = pulsar.getBookKeeperClient();\n    // use bk\n} catch (UnsupportedOperationException e) {\n    if (\"BookKeeper client is not available\".equals(e.getMessage())) {\n        // non-BookKeeper storage backend: use managed-ledger APIs instead\n    } else {\n        throw e;\n    }\n}","preventionTips":["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."],"tags":["bookkeeper","storage","unsupported-operation","broker-api"],"backgroundTag":"unsupported-operation","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}