apache/pulsar · error · NumberFormatException

Couldn't find ledgerid in path

Error message

Couldn't find ledgerid in path

What it means

getLedgerId extracts the numeric ledger id from an underreplication znode path using ID_EXTRACTION_PATTERN. If the regex finds no match (the path doesn't have the expected .../ledgers/region/xx/xx/ledgerId shape), it throws NumberFormatException with this message.

Source

Thrown at pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/PulsarLedgerUnderreplicationManager.java:221

                            || layout.getVersion() != LAYOUT_VERSION) {
                        throw new ReplicationException.CompatibilityException(
                                "Incompatible layout found (" + LAYOUT + ":" + LAYOUT_VERSION + ")");
                    }
                } catch (RuntimeException pe) {
                    throw new ReplicationException.CompatibilityException(
                            "Invalid data found", pe);
                }
                break;
            }
        }
    }

    private long getLedgerId(String path) throws NumberFormatException {
        Matcher m = ID_EXTRACTION_PATTERN.matcher(path);
        if (m.find()) {
            return Long.parseLong(m.group(1));
        } else {
            throw new NumberFormatException("Couldn't find ledgerid in path");
        }
    }

    private static String getParentPath(String base, long ledgerId) {
        String subdir1 = String.format("%04x", ledgerId >> 48 & 0xffff);
        String subdir2 = String.format("%04x", ledgerId >> 32 & 0xffff);
        String subdir3 = String.format("%04x", ledgerId >> 16 & 0xffff);
        String subdir4 = String.format("%04x", ledgerId & 0xffff);

        return String.format("%s/%s/%s/%s/%s",
                base, subdir1, subdir2, subdir3, subdir4);
    }

    public static String getUrLedgerPath(String base, long ledgerId) {
        return String.format("%s/urL%010d", getParentPath(base, ledgerId), ledgerId);
    }

    public static String getUrLedgerLockPath(String base, long ledgerId) {

View on GitHub (pinned to 820761864e)

Solutions

  1. Filter out special znodes (AbstractZkLedgerManager.isSpecialZnode or equivalent checks) before parsing paths.
  2. Verify the path matches the expected hierarchical pattern <base>/region/dd/dd/<ledgerId> produced by getParentPath.
  3. Fix the caller to only pass paths obtained from getLedgerList/underreplication hierarchy rather than arbitrary znode paths.
  4. If building paths yourself, use getParentPath-style formatting (String.format("%04x", ...)) rather than string concatenation.

Example fix

// before
long id = urManager.ledgerId(anyChildPath);
// after
if (AbstractZkLedgerManager.isSpecialZnode(anyChildPath)) {
    return; // skip layout/lock nodes
}
long id = urManager.ledgerId(anyChildPath);
Defensive patterns

Strategy: type-guard

Validate before calling

private static final Pattern LEDGER_PATH = Pattern.compile(".*/(\\d+)$");
boolean isLedgerPath(String p) { return LEDGER_PATH.matcher(p).matches(); }

Type guard

boolean isParseableLedgerPath(String path) {
    return path != null && ID_EXTRACTION_PATTERN.matcher(path).find();
}

Try / catch

try {
    long id = urManager.ledgerId(path);
} catch (NumberFormatException e) {
    log.debug("Skipping non-ledger znode path {}", path);
}

Prevention

When it happens

Trigger: Calling ledgerId/getLedgerId with a path that doesn't conform to the underreplication hierarchy — special znodes (locks, layout, management nodes) or arbitrary paths passed into the underreplication manager's path-parsing APIs.

Common situations: Iterating all children of the underreplication root without filtering special znodes; custom tooling constructing paths with wrong format; path changes between Pulsar versions breaking hand-written parsers.

Related errors


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