apache/pulsar · error · IOException

IOException

Error message

IOException

What it means

getLedgerIdFromGenPath() parses the ledger id out of the trailing segment of an ephemeral ledger-generation znode name. If the node name does not end in a parseable long after the ledger prefix (NumberFormatException), it is wrapped in an IOException. This indicates a corrupted or foreign znode inside the short-ledger-id generation path.

Source

Thrown at pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/PulsarLedgerIdGenerator.java:275

    }

    /**
     * Checks the existence of the long ledger id gen path. Existence indicates we have switched from the legacy
     * algorithm to the new method of generating 63-bit ids. If the existence is UNKNOWN, it looks in zk to
     * find out. If it previously checked in zk, it returns that value. This value changes when we run out
     * of ids < Integer.MAX_VALUE, and try to create the long ledger id gen path.
     */
    public CompletableFuture<Boolean> ledgerIdGenPathPresent() {
        return store.exists(ledgerIdGenPath);
    }

    private static long getLedgerIdFromGenPath(String nodeName, String ledgerPrefix) throws IOException {
        try {
            String[] parts = nodeName.split(ledgerPrefix);
            long ledgerId = Long.parseLong(parts[parts.length - 1]);
            return ledgerId;
        } catch (NumberFormatException e) {
            throw new IOException(e);
        }
    }

    private static String createLedgerPrefix(String ledgersPath, String idGenZnodeName) {
        String ledgerIdGenPath = null;
        if (StringUtils.isBlank(idGenZnodeName)) {
            ledgerIdGenPath = ledgersPath;
        } else {
            ledgerIdGenPath = ledgersPath + "/" + idGenZnodeName;
        }

        return ledgerIdGenPath + "/" + "ID-";
    }

    //If the config rootPath when use zk metadata store, it will append rootPath as the prefix of the path.
    //So when we get the path from the stat, we should truncate the rootPath.
    private String handleTheDeletePath(String path) {
        if (store instanceof DualMetadataStore dms) {

View on GitHub (pinned to 820761864e)

Solutions

  1. Inspect the znodes under the ledger id-generation path in ZooKeeper and delete/rename malformed nodes that do not match the expected ledger prefix pattern.
  2. Do not create arbitrary znodes inside the ledgers root; only BookKeeper should manage that subtree.
  3. If corruption came from a version migration, restore the metadata from backup or run the appropriate BookKeeper metadata repair tooling.
  4. Upgrade/patch: some BookKeeper versions harden this path; verify your metadata/bookkeeper versions match the supported matrix.

Example fix

// before
long ledgerId = ledgerIdGenerator.ledgerId(path); // IOException on malformed znode
// after
try {
    long ledgerId = ledgerIdGenerator.ledgerId(path);
} catch (IOException e) {
    LOG.error("Malformed ledger-id znode in generation path: {}", e.getMessage());
    // inspect and clean the offending znode in the metadata store
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate znode names before they reach the id generator
boolean isWellFormedLedgerNode(String nodeName, String prefix) {
    String suffix = nodeName.startsWith(prefix) ? nodeName.substring(prefix.length()) : "";
    return !suffix.isEmpty() && suffix.chars().allMatch(c -> Character.isDigit(c) || c == '-');
}

Type guard

Long tryParseLedgerId(String nodeName, String ledgerPrefix) {
    try {
        String[] parts = nodeName.split(ledgerPrefix);
        return Long.parseLong(parts[parts.length - 1]);
    } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
        return null; // malformed node
    }
}

Try / catch

try {
    long id = ledgerIdGenerator.ledgerId(genPath);
} catch (IOException e) {
    LOG.error("Malformed ledger id-gen znode; inspect metadata store path {}", genPath, e);
}

Prevention

When it happens

Trigger: internalGenerateShortLedgerId()/ledgerId() iterate children of the ledgersRootPath/idGenPath; a child znode whose name lacks the expected '<prefix>' + numeric-id format is passed to getLedgerIdFromGenPath(), producing NumberFormatException -> IOException.

Common situations: Manual znode creation or edits in the /ledgers path; leftover or malformed nodes from an aborted BookKeeper version migration; external tools writing non-ledger nodes into the idgen directory.

Related errors


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