{"record":{"id":"02cf83fae7406f3a","repo":"apache/pulsar","slug":"failed-to-get-schema-ledger-for-key","errorCode":null,"errorMessage":"Failed to get schema ledger for${key}","messagePattern":"Failed to get schema ledger for(.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/service/schema/BookkeeperSchemaStorage.java","lineNumber":207,"sourceCode":"            }\n            return Pair.of(locator, list);\n        });\n    }\n\n    CompletableFuture<Optional<LocatorEntry>> getLocator(String key) {\n        return getSchemaLocator(getSchemaPath(key));\n    }\n\n    public List<Long> getSchemaLedgerList(String key) throws IOException {\n        Optional<LocatorEntry> locatorEntry = null;\n        try {\n            locatorEntry = getLocator(key).get();\n        } catch (Exception e) {\n            log.warn()\n                    .attr(\"key\", key)\n                    .exceptionMessage(e)\n                    .log(\"Failed to get list of schema-storage ledger\");\n            throw new IOException(\"Failed to get schema ledger for\" + key);\n        }\n        LocatorEntry entry = locatorEntry.orElse(null);\n        if (entry == null) {\n            return null;\n        }\n        List<Long> ledgerIds = new ArrayList<>(entry.locator.getIndexsCount());\n        for (int i = 0; i < entry.locator.getIndexsCount(); i++) {\n            ledgerIds.add(entry.locator.getIndexAt(i).getPosition().getLedgerId());\n        }\n        return ledgerIds;\n    }\n\n    @VisibleForTesting\n    BookKeeper getBookKeeper() {\n        return bookKeeper;\n    }\n\n    @Override","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/schema/BookkeeperSchemaStorage.java#L189-L225","documentation":"BookkeeperSchemaStorage.getSchemaLedgerList(key) reads the schema locator for a topic's schema key to find the schema data ledgers. If obtaining the locator fails (the async getLocator future completes exceptionally), it logs a warning with the key and rethrows as IOException(\"Failed to get schema ledger for\" + key) — note the message has a cosmetic missing space before the key. This is an I/O-level failure reaching schema storage (BookKeeper/metastore), not a 'schema does not exist' result (that returns null).","triggerScenarios":"Calling getSchemaLedgerList (directly or via schemaLedgerId) when the underlying getLocator(key).get() future fails — BookKeeper client unavailable, ZooKeeper/metadata store errors, timeouts, or the schema storage ledger handle is broken.","commonSituations":"BookKeeper/ZooKeeper outage or network partition on the broker; schema storage metadata corrupted or deleted; timeouts under load; topic namespace whose schema storage was not initialized.","solutions":["Inspect the log warning 'Failed to get list of schema-storage ledger' (attr key, exceptionMessage) for the root cause","Verify BookKeeper and metadata-store (ZooKeeper) connectivity/health from the broker","Retry the schema read — transient BK/ZK faults often clear; the caller sees IOException so retry/backoff is appropriate","If persistent, check the schema-storage ledger metadata for the key and restore from backup or re-register the schema"],"exampleFix":"// before\ntry {\n    ledgerIds = storage.getSchemaLedgerList(key);\n} catch (IOException e) {\n    log.error(\"giving up: {}\", e.getMessage()); // 'Failed to get schema ledger forKey'\n}\n// after\ntry {\n    ledgerIds = storage.getSchemaLedgerList(key);\n} catch (IOException e) {\n    log.warn(\"schema storage unavailable for {}, will retry\", key, e);\n    ledgerIds = retryWithBackoff(() -> storage.getSchemaLedgerList(key), 3);\n}","handlingStrategy":"retry","validationCode":"// pre-check storage availability before schema reads\nif (!bkClient.getOwnedLedgersReadyToRead() /* or metadata store healthcheck */) {\n    throw new IOException(\"schema storage backend unavailable\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    List<Long> ledgerIds = storage.getSchemaLedgerList(key);\n} catch (IOException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Failed to get schema ledger for\")) {\n        // transient BK/ZK fault — retry with backoff; check the warn log 'Failed to get list of schema-storage ledger' for root cause\n        ledgerIds = retryWithBackoff(() -> storage.getSchemaLedgerList(key), 3, Duration.ofSeconds(1));\n    } else {\n        throw e;\n    }\n}","preventionTips":["Monitor BookKeeper and ZooKeeper health; alert on repeated 'Failed to get list of schema-storage ledger' warnings","Use bounded retries with backoff for schema-storage reads — a null return means 'no schema', only IOException means a real fault","Remember the message concatenation lacks a space ('for<key>') — match with startsWith, not exact strings","Verify schema-storage ledgers exist after restores/migrations of the metadata store"],"tags":["java","ioexception","bookkeeper","schema-storage"],"backgroundTag":"schema-storage-io-failure","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"}